Media::Player::Widget added instead of PlayerWidget.

New media player bar widget added. Switching between floating
media player panel and media player widget. New volume controller.
This commit is contained in:
John Preston 2016-10-12 22:34:25 +03:00
parent 8f135d7e00
commit 9eb8a93719
98 changed files with 2305 additions and 1841 deletions

View file

@ -2035,33 +2035,6 @@ botDescSkip: 8px;
suppressAll: 0.2;
suppressSong: 0.05;
playerHeight: 44px;
playerBg: #e4e9ef;
playerFg: #54748f;
playerTimeFg: #a4afba;
playerLineHeight: 3px;
playerMoverSize: size(2px, 7px);
playerLineActive: #6389a8;
playerLineInactive: #bac7d4;
playerSkip: 8px;
playerNameStyle: textStyle(defaultTextStyle) {
linkFg: #6389a8;
linkFgDown: #6389a8;
linkFlags: semiboldFont;
linkFlagsOver: semiboldFont;
}
playerPlay: sprite(377px, 109px, 19px, 22px);
playerPause: sprite(379px, 131px, 17px, 20px);
playerNext: sprite(374px, 151px, 22px, 14px);
playerPrev: sprite(374px, 165px, 22px, 14px);
playerClose: sprite(361px, 97px, 12px, 12px);
playerFull: sprite(365px, 109px, 12px, 12px);
playerRepeat: sprite(365px, 121px, 12px, 14px);
playerVolume: sprite(352px, 179px, 44px, 12px);
playerInactiveOpacity: 0.8;
playerUnavailableOpacity: 0.3;
playerDuration: 200;
inlineResultsLeft: 11px;
inlineResultsSkip: 3px;
inlineMediaHeight: 96px;

Binary file not shown.

After

Width:  |  Height:  |  Size: 133 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 197 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 188 B

After

Width:  |  Height:  |  Size: 219 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 297 B

After

Width:  |  Height:  |  Size: 319 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 188 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 297 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 349 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 547 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 182 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 292 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 182 B

After

Width:  |  Height:  |  Size: 201 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 292 B

After

Width:  |  Height:  |  Size: 325 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 299 B

After

Width:  |  Height:  |  Size: 264 B

View file

@ -400,9 +400,9 @@ namespace {
switch (user.type()) {
case mtpc_userEmpty: {
auto &d(user.c_userEmpty());
auto &d = user.c_userEmpty();
PeerId peer(peerFromUser(d.vid.v));
auto peer = peerFromUser(d.vid.v);
data = App::user(peer);
auto canShareThisContact = data->canShareThisContactFast();
wasContact = data->isContact();
@ -421,10 +421,10 @@ namespace {
if (wasContact != data->isContact()) update.flags |= UpdateFlag::UserIsContact;
} break;
case mtpc_user: {
auto &d(user.c_user());
auto &d = user.c_user();
minimal = d.is_min();
PeerId peer(peerFromUser(d.vid.v));
auto peer = peerFromUser(d.vid.v);
data = App::user(peer);
auto canShareThisContact = data->canShareThisContactFast();
wasContact = data->isContact();
@ -1095,11 +1095,11 @@ namespace {
}
bool checkEntitiesAndViewsUpdate(const MTPDmessage &m) {
PeerId peerId = peerFromMTP(m.vto_id);
auto peerId = peerFromMTP(m.vto_id);
if (m.has_from_id() && peerToUser(peerId) == MTP::authedId()) {
peerId = peerFromUser(m.vfrom_id);
}
if (HistoryItem *existing = App::histItemById(peerToChannel(peerId), m.vid.v)) {
if (auto existing = App::histItemById(peerToChannel(peerId), m.vid.v)) {
auto text = qs(m.vmessage);
auto entities = m.has_entities() ? entitiesFromMTP(m.ventities.c_vector().v) : EntitiesInText();
existing->setText({ text, entities });
@ -1951,7 +1951,7 @@ namespace {
HistoryItem *histItemById(ChannelId channelId, MsgId itemId) {
if (!itemId) return nullptr;
MsgsData *data = fetchMsgsData(channelId, false);
auto data = fetchMsgsData(channelId, false);
if (!data) return nullptr;
auto i = data->constFind(itemId);

View file

@ -21,6 +21,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#pragma once
#include "layerwidget.h"
#include "ui/widgets/shadow.h"
class BlueTitleShadow : public TWidget {
public:
@ -93,9 +94,9 @@ private:
};
class ScrollableBoxShadow : public PlainShadow {
class ScrollableBoxShadow : public Ui::PlainShadow {
public:
ScrollableBoxShadow(QWidget *parent) : PlainShadow(parent, st::boxScrollShadowBg) {
ScrollableBoxShadow(QWidget *parent) : Ui::PlainShadow(parent, st::boxScrollShadowBg) {
}
};

View file

@ -1359,25 +1359,25 @@ void StickersBox::setup() {
int bottomSkip = st::boxPadding.bottom();
if (_section == Section::Installed) {
_aboutHeight = st::stickersReorderPadding.top() + _about.countHeight(_aboutWidth) + st::stickersReorderPadding.bottom();
_topShadow = new PlainShadow(this, st::contactsAboutShadow);
_topShadow.create(this, st::contactsAboutShadow);
_save = new BoxButton(this, lang(lng_settings_save), st::defaultBoxButton);
_save.create(this, lang(lng_settings_save), st::defaultBoxButton);
connect(_save, SIGNAL(clicked()), this, SLOT(onSave()));
_cancel = new BoxButton(this, lang(lng_cancel), st::cancelBoxButton);
_cancel.create(this, lang(lng_cancel), st::cancelBoxButton);
connect(_cancel, SIGNAL(clicked()), this, SLOT(onClose()));
_bottomShadow = new ScrollableBoxShadow(this);
_bottomShadow.create(this);
bottomSkip = st::boxButtonPadding.top() + _save->height() + st::boxButtonPadding.bottom();
} else if (_section == Section::ArchivedPart) {
_aboutHeight = st::stickersReorderPadding.top() + _about.countHeight(_aboutWidth) + st::stickersReorderPadding.bottom();
_topShadow = new PlainShadow(this, st::contactsAboutShadow);
_topShadow.create(this, st::contactsAboutShadow);
_save = new BoxButton(this, lang(lng_box_ok), st::defaultBoxButton);
_save.create(this, lang(lng_box_ok), st::defaultBoxButton);
connect(_save, SIGNAL(clicked()), this, SLOT(onClose()));
} else if (_section == Section::Archived) {
_aboutHeight = st::stickersReorderPadding.top() + _about.countHeight(_aboutWidth) + st::stickersReorderPadding.bottom();
_topShadow = new PlainShadow(this, st::contactsAboutShadow);
_topShadow.create(this, st::contactsAboutShadow);
}
ItemListBox::init(_inner, bottomSkip, st::boxTitleHeight + _aboutHeight);
setMaxHeight(snap(countHeight(), int32(st::sessionsHeight), int32(st::boxMaxListHeight)));

View file

@ -24,6 +24,9 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "core/vector_of_moveable.h"
class ConfirmBox;
namespace Ui {
class PlainShadow;
} // namespace Ui
class StickerSetInner : public ScrolledWidget, public RPCSender, private base::Subscriber {
Q_OBJECT
@ -188,7 +191,7 @@ private:
ChildWidget<BoxButton> _cancel = { nullptr };
OrderedSet<mtpRequestId> _disenableRequests;
mtpRequestId _reorderRequest = 0;
ChildWidget<PlainShadow> _topShadow = { nullptr };
ChildWidget<Ui::PlainShadow> _topShadow = { nullptr };
ChildWidget<ScrollableBoxShadow> _bottomShadow = { nullptr };
QTimer _scrollTimer;

View file

@ -50,7 +50,7 @@ bool ClickHandler::setActive(const ClickHandlerPtr &p, ClickHandlerHost *host) {
}
}
if (p) {
_active.makeIfNull();
_active.createIfNull();
*_active = p;
if ((_activeHost = host)) {
bool emitClickHandlerActiveChanged = (!_pressed || !*_pressed || *_pressed == *_active);

View file

@ -93,7 +93,7 @@ public:
if (!_active || !*_active) {
return;
}
_pressed.makeIfNull();
_pressed.createIfNull();
*_pressed = *_active;
if ((_pressedHost = _activeHost)) {
_pressedHost->clickHandlerPressedChanged(*_pressed, true);

View file

@ -189,7 +189,10 @@ public:
std::swap(_p, other._p);
}
~unique_ptr() noexcept {
delete _p;
if (_p) {
delete _p;
_p = nullptr;
}
}
T &operator*() const {

View file

@ -450,15 +450,8 @@ public:
NeverFreedPointer(const NeverFreedPointer<T> &other) = delete;
NeverFreedPointer &operator=(const NeverFreedPointer<T> &other) = delete;
template <typename U>
void createIfNull(U creator) {
if (isNull()) {
reset(creator());
}
}
template <typename... Args>
void makeIfNull(Args&&... args) {
void createIfNull(Args&&... args) {
if (isNull()) {
reset(new T(std_::forward<Args>(args)...));
}

View file

@ -32,7 +32,7 @@ NeverFreedPointer<DataStructures> structures;
namespace internal {
void registerAbstractStructure(AbstractStructure **p) {
structures.makeIfNull();
structures.createIfNull();
structures->insert(p);
}

View file

@ -584,8 +584,11 @@ struct Data {
int32 DebugLoggingFlags = 0;
float64 RememberedSongVolume = kDefaultVolume;
float64 SongVolume = kDefaultVolume;
base::Observable<void> SongVolumeChanged;
float64 VideoVolume = kDefaultVolume;
base::Observable<void> VideoVolumeChanged;
// config
int32 ChatSizeMax = 200;
@ -694,8 +697,11 @@ DefineVar(Global, bool, ScreenIsLocked);
DefineVar(Global, int32, DebugLoggingFlags);
DefineVar(Global, float64, RememberedSongVolume);
DefineVar(Global, float64, SongVolume);
DefineRefVar(Global, base::Observable<void>, SongVolumeChanged);
DefineVar(Global, float64, VideoVolume);
DefineRefVar(Global, base::Observable<void>, VideoVolumeChanged);
// config
DefineVar(Global, int32, ChatSizeMax);

View file

@ -252,8 +252,6 @@ bool started();
void start();
void finish();
constexpr float64 kDefaultVolume = 0.9;
DeclareReadOnlyVar(uint64, LaunchId);
DeclareRefVar(SingleDelayedCall, HandleHistoryUpdate);
DeclareRefVar(SingleDelayedCall, HandleUnreadCounterUpdate);
@ -273,8 +271,13 @@ DeclareVar(bool, ScreenIsLocked);
DeclareVar(int32, DebugLoggingFlags);
constexpr float64 kDefaultVolume = 0.9;
DeclareVar(float64, RememberedSongVolume);
DeclareVar(float64, SongVolume);
DeclareRefVar(base::Observable<void>, SongVolumeChanged);
DeclareVar(float64, VideoVolume);
DeclareRefVar(base::Observable<void>, VideoVolumeChanged);
// config
DeclareVar(int32, ChatSizeMax);

View file

@ -25,9 +25,9 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "mainwidget.h"
#include "mainwindow.h"
#include "localstorage.h"
#include "playerwidget.h"
#include "media/media_audio.h"
#include "media/media_clip_reader.h"
#include "media/player/media_player_instance.h"
#include "boxes/confirmbox.h"
#include "boxes/addcontactbox.h"
#include "core/click_handler_types.h"
@ -1463,8 +1463,8 @@ bool HistoryDocument::updateStatusText() const {
showPause = (playbackState.state == AudioPlayerPlaying || playbackState.state == AudioPlayerResuming || playbackState.state == AudioPlayerStarting);
} else {
}
if (!showPause && (playing == AudioMsgId(_data, _parent->fullId())) && App::main() && App::main()->player()->seekingSong(playing)) {
showPause = true;
if (!showPause && (playing == AudioMsgId(_data, _parent->fullId()))) {
showPause = (Media::Player::exists() && Media::Player::instance()->isSeeking());
}
}
}

View file

@ -51,7 +51,6 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "window/top_bar_widget.h"
#include "window/chat_background.h"
#include "observer_peer.h"
#include "playerwidget.h"
#include "core/qthelp_regex.h"
namespace {
@ -3124,7 +3123,7 @@ HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent)
_attachDragDocument->hide();
_attachDragPhoto->hide();
_topShadow.hide();
_topShadow->hide();
connect(_attachDragDocument, SIGNAL(dropped(const QMimeData*)), this, SLOT(onDocumentDrop(const QMimeData*)));
connect(_attachDragPhoto, SIGNAL(dropped(const QMimeData*)), this, SLOT(onPhotoDrop(const QMimeData*)));
@ -4474,7 +4473,7 @@ bool HistoryWidget::canWriteMessage() const {
void HistoryWidget::updateControlsVisibility() {
if (!_a_show.animating()) {
_topShadow.setVisible(_peer ? true : false);
_topShadow->setVisible(_peer ? true : false);
}
updateToEndVisibility();
if (!_history || _a_show.animating()) {
@ -4501,15 +4500,15 @@ void HistoryWidget::updateControlsVisibility() {
_attachType->hide();
_emojiPan->hide();
if (_pinnedBar) {
_pinnedBar->cancel.hide();
_pinnedBar->shadow.hide();
_pinnedBar->cancel->hide();
_pinnedBar->shadow->hide();
}
return;
}
if (_pinnedBar) {
_pinnedBar->cancel.show();
_pinnedBar->shadow.show();
_pinnedBar->cancel->show();
_pinnedBar->shadow->show();
}
if (_firstLoadRequest && !_scroll.isHidden()) {
_scroll.hide();
@ -5431,11 +5430,11 @@ void HistoryWidget::showAnimated(Window::SlideDirection direction, const Window:
_cacheUnder = params.oldContentCache;
show();
_topShadow.setVisible(params.withTopBarShadow ? false : true);
_topShadow->setVisible(params.withTopBarShadow ? false : true);
_historyToEnd->finishAnimation();
_cacheOver = App::main()->grabForShowAnimation(params);
App::main()->topBar()->startAnim();
_topShadow.setVisible(params.withTopBarShadow ? true : false);
_topShadow->setVisible(params.withTopBarShadow ? true : false);
_scroll.hide();
_kbScroll.hide();
@ -5458,8 +5457,8 @@ void HistoryWidget::showAnimated(Window::SlideDirection direction, const Window:
_joinChannel.hide();
_muteUnmute.hide();
if (_pinnedBar) {
_pinnedBar->shadow.hide();
_pinnedBar->cancel.hide();
_pinnedBar->shadow->hide();
_pinnedBar->cancel->hide();
}
int delta = st::slideShift;
@ -5484,7 +5483,7 @@ void HistoryWidget::step_show(float64 ms, bool timer) {
float64 dt = ms / st::slideDuration;
if (dt >= 1) {
_a_show.stop();
_topShadow.setVisible(_peer ? true : false);
_topShadow->setVisible(_peer ? true : false);
_historyToEnd->finishAnimation();
a_coordUnder.finish();
@ -5525,7 +5524,7 @@ void HistoryWidget::doneShow() {
void HistoryWidget::animStop() {
if (!_a_show.animating()) return;
_a_show.stop();
_topShadow.setVisible(_peer ? true : false);
_topShadow->setVisible(_peer ? true : false);
_historyToEnd->finishAnimation();
}
@ -6916,6 +6915,17 @@ void HistoryWidget::peerMessagesUpdated() {
if (_list) peerMessagesUpdated(_peer->id);
}
void HistoryWidget::grapWithoutTopBarShadow() {
grabStart();
_topShadow->hide();
}
void HistoryWidget::grabFinish() {
_inGrab = false;
resizeEvent(0);
_topShadow->show();
}
bool HistoryWidget::isItemVisible(HistoryItem *item) {
if (isHidden() || _a_show.animating() || !_list) {
return false;
@ -6993,8 +7003,8 @@ void HistoryWidget::resizeEvent(QResizeEvent *e) {
_reportSpamPanel.move(0, st::replyHeight);
_fieldAutocomplete->setBoundings(_scroll.geometry());
}
_pinnedBar->cancel.move(width() - _pinnedBar->cancel.width(), 0);
_pinnedBar->shadow.setGeometry(0, st::replyHeight, width(), st::lineWidth);
_pinnedBar->cancel->move(width() - _pinnedBar->cancel->width(), 0);
_pinnedBar->shadow->setGeometry(0, st::replyHeight, width(), st::lineWidth);
} else if (_scroll.y() != 0) {
_scroll.move(0, 0);
_reportSpamPanel.move(0, 0);
@ -7029,8 +7039,8 @@ void HistoryWidget::resizeEvent(QResizeEvent *e) {
break;
}
_topShadow.resize(width() - ((!Adaptive::OneColumn() && !_inGrab) ? st::lineWidth : 0), st::lineWidth);
_topShadow.moveToLeft((!Adaptive::OneColumn() && !_inGrab) ? st::lineWidth : 0, 0);
_topShadow->resize(width() - ((!Adaptive::OneColumn() && !_inGrab) ? st::lineWidth : 0), st::lineWidth);
_topShadow->moveToLeft((!Adaptive::OneColumn() && !_inGrab) ? st::lineWidth : 0, 0);
}
void HistoryWidget::itemRemoved(HistoryItem *item) {
@ -7532,7 +7542,6 @@ void HistoryWidget::onInlineResultSend(InlineBots::Result *result, UserData *bot
HistoryWidget::PinnedBar::PinnedBar(MsgId msgId, HistoryWidget *parent)
: msgId(msgId)
, msg(0)
, cancel(parent, st::replyCancel)
, shadow(parent, st::shadowColor) {
}
@ -7582,15 +7591,15 @@ bool HistoryWidget::pinnedMsgVisibilityUpdated() {
if (!_pinnedBar) {
_pinnedBar = new PinnedBar(pinnedMsgId, this);
if (_a_show.animating()) {
_pinnedBar->cancel.hide();
_pinnedBar->shadow.hide();
_pinnedBar->cancel->hide();
_pinnedBar->shadow->hide();
} else {
_pinnedBar->cancel.show();
_pinnedBar->shadow.show();
_pinnedBar->cancel->show();
_pinnedBar->shadow->show();
}
connect(&_pinnedBar->cancel, SIGNAL(clicked()), this, SLOT(onPinnedHide()));
connect(_pinnedBar->cancel, SIGNAL(clicked()), this, SLOT(onPinnedHide()));
_reportSpamPanel.raise();
_topShadow.raise();
_topShadow->raise();
if (_membersDropdown) {
_membersDropdown->raise();
}
@ -8676,11 +8685,11 @@ void HistoryWidget::drawPinnedBar(Painter &p) {
p.drawText(left, st::msgReplyPadding.top() + st::msgServiceNameFont->ascent, lang(lng_pinned_message));
p.setPen((((_pinnedBar->msg->toHistoryMessage() && _pinnedBar->msg->toHistoryMessage()->emptyText()) || _pinnedBar->msg->serviceMsg()) ? st::msgInDateFg : st::msgColor)->p);
_pinnedBar->text.drawElided(p, left, st::msgReplyPadding.top() + st::msgServiceNameFont->height, width() - left - _pinnedBar->cancel.width() - st::msgReplyPadding.right());
_pinnedBar->text.drawElided(p, left, st::msgReplyPadding.top() + st::msgServiceNameFont->height, width() - left - _pinnedBar->cancel->width() - st::msgReplyPadding.right());
} else {
p.setFont(st::msgDateFont);
p.setPen(st::msgInDateFg);
p.drawText(left, st::msgReplyPadding.top() + (st::msgReplyBarSize.height() - st::msgDateFont->height) / 2 + st::msgDateFont->ascent, st::msgDateFont->elided(lang(lng_profile_loading), width() - left - _pinnedBar->cancel.width() - st::msgReplyPadding.right()));
p.drawText(left, st::msgReplyPadding.top() + (st::msgReplyBarSize.height() - st::msgDateFont->height) / 2 + st::msgDateFont->ascent, st::msgDateFont->elided(lang(lng_profile_loading), width() - left - _pinnedBar->cancel->width() - st::msgReplyPadding.right()));
}
}
@ -8715,7 +8724,7 @@ void HistoryWidget::paintEvent(QPaintEvent *e) {
}
QRect fill(0, 0, width(), App::main()->height());
int fromy = (hasTopBar ? (-st::topBarHeight) : 0), x = 0, y = 0;
int fromy = App::main()->backgroundFromY(), x = 0, y = 0;
QPixmap cached = App::main()->cachedBackground(fill, x, y);
if (cached.isNull()) {
auto &pix = Window::chatBackground()->image();
@ -8751,7 +8760,7 @@ void HistoryWidget::paintEvent(QPaintEvent *e) {
if (_recording) drawRecording(p);
}
}
if (_pinnedBar && !_pinnedBar->cancel.isHidden()) {
if (_pinnedBar && !_pinnedBar->cancel->isHidden()) {
drawPinnedBar(p);
}
if (_scroll.isHidden()) {

View file

@ -38,6 +38,7 @@ class Result;
namespace Ui {
class HistoryDownButton;
class InnerDropdown;
class PlainShadow;
} // namespace Ui
class Dropdown;
@ -530,7 +531,6 @@ class HistoryWidget : public TWidget, public RPCSender, private base::Subscriber
Q_OBJECT
public:
HistoryWidget(QWidget *parent);
void start();
@ -701,15 +701,8 @@ public:
_inGrab = true;
resizeEvent(0);
}
void grapWithoutTopBarShadow() {
grabStart();
_topShadow.hide();
}
void grabFinish() override {
_inGrab = false;
resizeEvent(0);
_topShadow.show();
}
void grapWithoutTopBarShadow();
void grabFinish() override;
bool isItemVisible(HistoryItem *item);
@ -902,8 +895,8 @@ private:
MsgId msgId = 0;
HistoryItem *msg = nullptr;
Text text;
IconedButton cancel;
PlainShadow shadow;
ChildWidget<IconedButton> cancel;
ChildWidget<Ui::PlainShadow> shadow;
};
PinnedBar *_pinnedBar = nullptr;
void updatePinnedBar(bool force = false);
@ -1163,7 +1156,7 @@ private:
bool _saveDraftText = false;
QTimer _saveDraftTimer, _saveCloudDraftTimer;
PlainShadow _topShadow;
ChildWidget<Ui::PlainShadow> _topShadow;
bool _inGrab = false;
};

View file

@ -24,13 +24,13 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "styles/style_overview.h"
#include "styles/style_history.h"
#include "inline_bots/inline_bot_result.h"
#include "media/media_audio.h"
#include "media/media_clip_reader.h"
#include "media/player/media_player_instance.h"
#include "history/history_location_manager.h"
#include "localstorage.h"
#include "mainwidget.h"
#include "lang.h"
#include "playerwidget.h"
namespace InlineBots {
namespace Layout {

View file

@ -209,7 +209,7 @@ const DocumentItems *documentItems() {
namespace internal {
void regDocumentItem(DocumentData *document, ItemBase *item) {
documentItemsMap.makeIfNull();
documentItemsMap.createIfNull();
(*documentItemsMap)[document].insert(item);
}

View file

@ -232,7 +232,7 @@ bool Result::onChoose(Layout::ItemBase *layout) {
} else if (_document->loading()) {
_document->cancel();
} else {
DocumentOpenClickHandler::doOpen(_document, ActionOnLoadNone);
DocumentOpenClickHandler::doOpen(_document, nullptr, ActionOnLoadNone);
}
return false;
}

View file

@ -27,7 +27,6 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "fileuploader.h"
#include "mainwindow.h"
#include "ui/filedialog.h"
#include "playerwidget.h"
#include "boxes/addcontactbox.h"
#include "boxes/confirmbox.h"
#include "media/media_audio.h"

View file

@ -175,29 +175,22 @@ void TaskQueueWorker::onTaskAdded() {
FileLoadTask::FileLoadTask(const QString &filepath, PrepareMediaType type, const FileLoadTo &to, FileLoadForceConfirmType confirm) : _id(rand_value<uint64>())
, _to(to)
, _filepath(filepath)
, _duration(0)
, _type(type)
, _confirm(confirm)
, _result(0) {
, _confirm(confirm) {
}
FileLoadTask::FileLoadTask(const QByteArray &content, PrepareMediaType type, const FileLoadTo &to) : _id(rand_value<uint64>())
, _to(to)
, _content(content)
, _duration(0)
, _type(type)
, _confirm(FileLoadNoForceConfirm)
, _result(0) {
, _type(type) {
}
FileLoadTask::FileLoadTask(const QImage &image, PrepareMediaType type, const FileLoadTo &to, FileLoadForceConfirmType confirm, const QString &originalText) : _id(rand_value<uint64>())
, _to(to)
, _image(image)
, _duration(0)
, _type(type)
, _confirm(confirm)
, _originalText(originalText)
, _result(0) {
, _originalText(originalText) {
}
FileLoadTask::FileLoadTask(const QByteArray &voice, int32 duration, const VoiceWaveform &waveform, const FileLoadTo &to) : _id(rand_value<uint64>())
@ -205,9 +198,7 @@ FileLoadTask::FileLoadTask(const QByteArray &voice, int32 duration, const VoiceW
, _content(voice)
, _duration(duration)
, _waveform(waveform)
, _type(PrepareAudio)
, _confirm(FileLoadNoForceConfirm)
, _result(0) {
, _type(PrepareAudio) {
}
void FileLoadTask::process() {

View file

@ -263,10 +263,10 @@ protected:
QString _filepath;
QImage _image;
QByteArray _content;
int32 _duration;
int32 _duration = 0;
VoiceWaveform _waveform;
PrepareMediaType _type;
FileLoadForceConfirmType _confirm;
FileLoadForceConfirmType _confirm = FileLoadNoForceConfirm;
QString _originalText;
FileLoadResultPtr _result;

View file

@ -24,6 +24,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "styles/style_dialogs.h"
#include "ui/buttons/peer_avatar_button.h"
#include "ui/buttons/round_button.h"
#include "ui/widgets/shadow.h"
#include "window/section_memento.h"
#include "window/section_widget.h"
#include "window/top_bar_widget.h"
@ -34,7 +35,6 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "dialogswidget.h"
#include "historywidget.h"
#include "overviewwidget.h"
#include "playerwidget.h"
#include "lang.h"
#include "boxes/addcontactbox.h"
#include "fileuploader.h"
@ -50,11 +50,14 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "localstorage.h"
#include "shortcuts.h"
#include "media/media_audio.h"
#include "media/player/media_player_panel.h"
#include "media/player/media_player_widget.h"
#include "media/player/media_player_volume_controller.h"
#include "media/player/media_player_instance.h"
#include "core/qthelp_regex.h"
#include "core/qthelp_url.h"
#include "window/chat_background.h"
#include "window/player_wrap_widget.h"
StackItemSection::StackItemSection(std_::unique_ptr<Window::SectionMemento> &&memento) : StackItem(nullptr)
, _memento(std_::move(memento)) {
@ -63,15 +66,12 @@ StackItemSection::StackItemSection(std_::unique_ptr<Window::SectionMemento> &&me
StackItemSection::~StackItemSection() {
}
#include "boxes/confirmphonebox.h"
MainWidget::MainWidget(MainWindow *window) : TWidget(window)
, _a_show(animation(this, &MainWidget::step_show))
, _dialogsWidth(st::dialogsWidthMin)
, _sideShadow(this, st::shadowColor)
, _dialogs(this)
, _history(this)
, _player(this)
, _topBar(this)
, _mediaType(this)
, _api(new ApiWrap(this)) {
@ -131,8 +131,6 @@ MainWidget::MainWidget(MainWindow *window) : TWidget(window)
App::wnd()->getTitle()->updateControlsVisibility();
_topBar->hide();
_player->hidePlayer();
orderWidgets();
MTP::setGlobalFailHandler(rpcFail(&MainWidget::updateFail));
@ -1344,7 +1342,6 @@ void MainWidget::overviewPreloaded(PeerData *peer, const MTPmessages_Messages &r
}
void MainWidget::mediaOverviewUpdated(PeerData *peer, MediaOverviewType type) {
if (!_player->isHidden()) _player->mediaOverviewUpdated(peer, type);
if (_overview && (_overview->peer() == peer || _overview->peer()->migrateFrom() == peer)) {
_overview->mediaOverviewUpdated(peer, type);
@ -1581,24 +1578,8 @@ void MainWidget::handleAudioUpdate(const AudioMsgId &audioId) {
}
if (playing == audioId && audioId.type() == AudioMsgId::Type::Song) {
if (!_mediaPlayer && Media::Player::exists()) {
_mediaPlayer.create(this);
updateMediaPlayerPosition();
orderWidgets();
Media::Player::instance()->createdNotifier().notify(Media::Player::CreatedEvent(_mediaPlayer), true);
}
_player->updateState(playing, playbackState);
if (!(playbackState.state & AudioPlayerStoppedMask) && playbackState.state != AudioPlayerFinishing) {
if (!_player->isOpened()) {
_player->openPlayer();
if (_player->isHidden() && !_a_show.animating()) {
_player->showPlayer();
_playerHeight = _contentScrollAddToY = _player->height();
resizeEvent(0);
}
}
if (!_playerPanel && !_player && Media::Player::exists()) {
createPlayer();
}
}
@ -1612,18 +1593,62 @@ void MainWidget::handleAudioUpdate(const AudioMsgId &audioId) {
}
}
void MainWidget::closePlayer() {
if (_player->isOpened()) {
_player->closePlayer();
if (!_player->isHidden() && !_a_show.animating()) {
_player->hidePlayer();
_contentScrollAddToY = -_player->height();
_playerHeight = 0;
resizeEvent(0);
void MainWidget::switchToPanelPlayer() {
_player->slideUp();
_playerVolume.destroyDelayed();
if (!_playerPanel) {
_playerPanel.create(this, Media::Player::Panel::Layout::Full);
_playerPanel->setPinCallback([this] { switchToFixedPlayer(); });
updateMediaPlayerPosition();
orderWidgets();
Media::Player::instance()->createdNotifier().notify(Media::Player::PanelEvent(_playerPanel), true);
}
}
void MainWidget::switchToFixedPlayer() {
_playerPanel.destroyDelayed();
if (!_player) {
createPlayer();
} else {
_player->slideDown();
if (!_playerVolume) {
_playerVolume.create(this);
_player->entity()->volumeWidgetCreated(_playerVolume);
updateMediaPlayerPosition();
}
}
}
void MainWidget::createPlayer() {
_player.create(this, [this] { playerHeightUpdated(); });
_player->entity()->setCloseCallback([this] { switchToPanelPlayer(); });
_playerVolume.create(this);
_player->entity()->volumeWidgetCreated(_playerVolume);
orderWidgets();
if (_a_show.animating()) {
_player->showFast();
_player->hide();
} else {
_player->hideFast();
_player->slideDown();
_playerHeight = _contentScrollAddToY = _player->contentHeight();
updateControlsGeometry();
}
}
void MainWidget::playerHeightUpdated() {
auto playerHeight = _player->contentHeight();
if (playerHeight != _playerHeight) {
_contentScrollAddToY += playerHeight - _playerHeight;
_playerHeight = playerHeight;
updateControlsGeometry();
}
if (_playerPanel && !_playerHeight && _player->isHidden()) {
_playerVolume.destroyDelayed();
_player.destroyDelayed();
}
}
void MainWidget::documentLoadProgress(FileLoader *loader) {
if (auto mtpLoader = loader ? loader->mtpLoader() : nullptr) {
documentLoadProgress(App::document(mtpLoader->objId()));
@ -1645,13 +1670,6 @@ void MainWidget::documentLoadProgress(DocumentData *document) {
App::wnd()->documentUpdated(document);
if (!document->loaded() && document->song()) {
if (audioPlayer() && document->loading()) {
AudioMsgId playing;
auto playbackState = audioPlayer()->currentState(&playing, AudioMsgId::Type::Song);
if (playing.audio() == document && !_player->isHidden()) {
_player->updateState(playing, playbackState);
}
}
if (Media::Player::exists()) {
Media::Player::instance()->documentLoadProgress(document);
}
@ -2314,6 +2332,9 @@ Window::SectionSlideParams MainWidget::prepareShowAnimation(bool willHaveTopBarS
result.withTopBarShadow = false;
}
if (_player) {
_player->hideShadow();
}
if (selectingPeer() && Adaptive::OneColumn()) {
result.oldContentCache = myGrab(this, QRect(0, _playerHeight, _dialogsWidth, height() - _playerHeight));
} else if (_wideSection) {
@ -2329,13 +2350,16 @@ Window::SectionSlideParams MainWidget::prepareShowAnimation(bool willHaveTopBarS
if (Adaptive::OneColumn()) {
result.oldContentCache = myGrab(this, QRect(0, _playerHeight, _dialogsWidth, height() - _playerHeight));
} else {
_sideShadow.hide();
_sideShadow->hide();
result.oldContentCache = myGrab(this, QRect(_dialogsWidth, _playerHeight, width() - _dialogsWidth, height() - _playerHeight));
_sideShadow.show();
_sideShadow->show();
}
if (_overview) _overview->grabFinish();
_history->grabFinish();
}
if (_player) {
_player->showShadow();
}
return result;
}
@ -2432,11 +2456,16 @@ void MainWidget::showBackFromStack() {
void MainWidget::orderWidgets() {
_topBar->raise();
_player->raise();
_dialogs->raise();
if (_player) {
_player->raise();
}
if (_playerVolume) {
_playerVolume->raise();
}
_mediaType->raise();
_sideShadow.raise();
if (_mediaPlayer) _mediaPlayer->raise();
_sideShadow->raise();
if (_playerPanel) _playerPanel->raise();
if (_hider) _hider->raise();
}
@ -2450,12 +2479,18 @@ QRect MainWidget::historyRect() const {
QPixmap MainWidget::grabForShowAnimation(const Window::SectionSlideParams &params) {
_topBar->stopAnim();
QPixmap result;
if (_player) {
_player->hideShadow();
}
if (Adaptive::OneColumn()) {
result = myGrab(this, QRect(0, _playerHeight, _dialogsWidth, height() - _playerHeight));
} else {
_sideShadow.hide();
_sideShadow->hide();
result = myGrab(this, QRect(_dialogsWidth, _playerHeight, width() - _dialogsWidth, height() - _playerHeight));
_sideShadow.show();
_sideShadow->show();
}
if (_player) {
_player->showShadow();
}
return result;
}
@ -2602,11 +2637,11 @@ void MainWidget::hideAll() {
if (_overview) {
_overview->hide();
}
_sideShadow.hide();
_sideShadow->hide();
_topBar->hide();
_mediaType->hide();
if (_player->isOpened() && !_player->isHidden()) {
_player->hidePlayer();
if (_player) {
_player->hide();
_playerHeight = 0;
}
}
@ -2617,7 +2652,7 @@ void MainWidget::showAll() {
Ui::showLayer(new InformBox(lang(lng_signin_password_removed)));
}
if (Adaptive::OneColumn()) {
_sideShadow.hide();
_sideShadow->hide();
if (_hider) {
_hider->hide();
if (!_forwardConfirm && _hider->wasOffered()) {
@ -2654,7 +2689,7 @@ void MainWidget::showAll() {
}
}
} else {
_sideShadow.show();
_sideShadow->show();
if (_hider) {
_hider->show();
if (_forwardConfirm) {
@ -2677,9 +2712,9 @@ void MainWidget::showAll() {
_topBar->show();
}
}
if (_player->isOpened() && _player->isHidden()) {
_player->showPlayer();
_playerHeight = _player->height();
if (_player) {
_player->show();
_playerHeight = _player->contentHeight();
}
resizeEvent(0);
@ -2695,30 +2730,35 @@ inline int chatsListWidth(int windowWidth) {
} // namespace
void MainWidget::resizeEvent(QResizeEvent *e) {
int32 tbh = _topBar->isHidden() ? 0 : st::topBarHeight;
updateMediaPlayerPosition();
updateControlsGeometry();
}
void MainWidget::updateControlsGeometry() {
auto tbh = _topBar->isHidden() ? 0 : st::topBarHeight;
if (Adaptive::OneColumn()) {
_dialogsWidth = width();
_player->setGeometry(0, 0, _dialogsWidth, _player->height());
if (_player) {
_player->resizeToWidth(_dialogsWidth);
_player->moveToLeft(0, 0);
}
_dialogs->setGeometry(0, _playerHeight, _dialogsWidth, height() - _playerHeight);
_topBar->setGeometry(0, _playerHeight, _dialogsWidth, st::topBarHeight);
_history->setGeometry(0, _playerHeight + tbh, _dialogsWidth, height() - _playerHeight - tbh);
if (_hider) _hider->setGeometry(0, 0, _dialogsWidth, height());
} else {
_dialogsWidth = chatsListWidth(width());
_dialogs->resize(_dialogsWidth, height());
_dialogs->moveToLeft(0, 0);
_sideShadow.resize(st::lineWidth, height());
_sideShadow.moveToLeft(_dialogsWidth, 0);
_player->resize(width() - _dialogsWidth, _player->height());
_player->moveToLeft(_dialogsWidth, 0);
_topBar->resize(width() - _dialogsWidth, st::topBarHeight);
_topBar->moveToLeft(_dialogsWidth, _playerHeight);
_history->resize(width() - _dialogsWidth, height() - _playerHeight - tbh);
_history->moveToLeft(_dialogsWidth, _playerHeight + tbh);
auto sectionWidth = width() - _dialogsWidth;
_dialogs->setGeometryToLeft(0, 0, _dialogsWidth, height());
_sideShadow->setGeometryToLeft(_dialogsWidth, 0, st::lineWidth, height());
if (_player) {
_player->resizeToWidth(sectionWidth);
_player->moveToLeft(_dialogsWidth, 0);
}
_topBar->setGeometryToLeft(_dialogsWidth, _playerHeight, sectionWidth, st::topBarHeight);
_history->setGeometryToLeft(_dialogsWidth, _playerHeight + tbh, sectionWidth, height() - _playerHeight - tbh);
if (_hider) {
_hider->resize(width() - _dialogsWidth, height());
_hider->moveToLeft(_dialogsWidth, 0);
_hider->setGeometryToLeft(_dialogsWidth, 0, sectionWidth, height());
}
}
_mediaType->moveToLeft(width() - _mediaType->width(), _playerHeight + st::topBarHeight);
@ -2727,12 +2767,18 @@ void MainWidget::resizeEvent(QResizeEvent *e) {
_wideSection->setGeometryWithTopMoved(wideSectionGeometry, _contentScrollAddToY);
}
if (_overview) _overview->setGeometry(_history->geometry());
updateMediaPlayerPosition();
_contentScrollAddToY = 0;
}
void MainWidget::updateMediaPlayerPosition() {
if (_mediaPlayer) {
_mediaPlayer->moveToRight(0, 0);
if (_playerPanel) {
_playerPanel->moveToRight(0, 0);
}
if (_playerVolume && _player) {
auto relativePosition = _player->entity()->getPositionForVolumeWidget();
auto playerMargins = _playerVolume->getMargin();
_playerVolume->moveToLeft(_player->x() + relativePosition.x() - playerMargins.left(), _player->y() + relativePosition.y() - playerMargins.top());
}
}
@ -2745,7 +2791,10 @@ void MainWidget::keyPressEvent(QKeyEvent *e) {
void MainWidget::updateAdaptiveLayout() {
showAll();
_sideShadow.setVisible(!Adaptive::OneColumn());
_sideShadow->setVisible(!Adaptive::OneColumn());
if (_player) {
_player->updateAdaptiveLayout();
}
}
bool MainWidget::needBackButton() {
@ -2807,8 +2856,8 @@ Window::TopBarWidget *MainWidget::topBar() {
return _topBar;
}
PlayerWidget *MainWidget::player() {
return _player;
int MainWidget::backgroundFromY() const {
return (_topBar->isHidden() ? 0 : (-st::topBarHeight)) - _playerHeight;
}
void MainWidget::onTopBarClick() {
@ -3486,6 +3535,8 @@ void MainWidget::onSelfParticipantUpdated(ChannelData *channel) {
bool MainWidget::contentOverlapped(const QRect &globalRect) {
return (_history->contentOverlapped(globalRect) ||
(_playerPanel && _playerPanel->overlaps(globalRect)) ||
(_playerVolume && _playerVolume->overlaps(globalRect)) ||
_mediaType->overlaps(globalRect));
}

View file

@ -31,14 +31,18 @@ class Row;
namespace Media {
namespace Player {
class Widget;
class VolumeWidget;
class Panel;
} // namespace Player
} // namespace Media
namespace Ui {
class PeerAvatarButton;
class PlainShadow;
} // namespace Ui
namespace Window {
class PlayerWrapWidget;
class TopBarWidget;
class SectionMemento;
class SectionWidget;
@ -51,7 +55,6 @@ class ConfirmBox;
class DialogsWidget;
class HistoryWidget;
class OverviewWidget;
class PlayerWidget;
class HistoryHider;
class Dropdown;
@ -128,9 +131,7 @@ enum NotifySettingStatus {
namespace InlineBots {
namespace Layout {
class ItemBase;
} // namespace Layout
} // namespace InlineBots
@ -147,8 +148,8 @@ public:
QRect getMembersShowAreaGeometry() const;
void setMembersShowAreaActive(bool active);
Window::TopBarWidget *topBar();
int backgroundFromY() const;
PlayerWidget *player();
int contentScrollAddToY() const;
void animShow(const QPixmap &bgAnimCache, bool back = false);
@ -379,8 +380,6 @@ public:
bool isItemVisible(HistoryItem *item);
void closePlayer();
void documentLoadProgress(DocumentData *document);
void app_sendBotCallback(const HistoryMessageReplyMarkup::Button *button, const HistoryItem *msg, int row, int col);
@ -488,6 +487,12 @@ private:
void updateAdaptiveLayout();
void handleAudioUpdate(const AudioMsgId &audioId);
void updateMediaPlayerPosition();
void updateControlsGeometry();
void createPlayer();
void switchToPanelPlayer();
void switchToFixedPlayer();
void playerHeightUpdated();
void sendReadRequest(PeerData *peer, MsgId upTo);
void channelReadDone(PeerData *peer, const MTPBool &result);
@ -581,15 +586,15 @@ private:
int _dialogsWidth;
PlainShadow _sideShadow;
ChildWidget<Ui::PlainShadow> _sideShadow;
ChildWidget<DialogsWidget> _dialogs;
ChildWidget<HistoryWidget> _history;
ChildWidget<Window::SectionWidget> _wideSection = { nullptr };
ChildWidget<OverviewWidget> _overview = { nullptr };
ChildWidget<PlayerWidget> _player;
ChildWidget<Window::TopBarWidget> _topBar;
ChildWidget<Media::Player::Widget> _mediaPlayer = { nullptr };
ChildWidget<Window::PlayerWrapWidget> _player = { nullptr };
ChildWidget<Media::Player::VolumeWidget> _playerVolume = { nullptr };
ChildWidget<Media::Player::Panel> _playerPanel = { nullptr };
ConfirmBox *_forwardConfirm = nullptr; // for single column layout
ChildWidget<HistoryHider> _hider = { nullptr };
std_::vector_of_moveable<std_::unique_ptr<StackItem>> _stack;

View file

@ -286,21 +286,21 @@ void AudioPlayer::AudioMsg::clear() {
videoPlayId = 0;
}
AudioPlayer::AudioPlayer() : _audioCurrent(0), _songCurrent(0),
_fader(new AudioPlayerFader(&_faderThread)),
_loader(new AudioPlayerLoaders(&_loaderThread)) {
AudioPlayer::AudioPlayer()
: _fader(new AudioPlayerFader(&_faderThread))
, _loader(new AudioPlayerLoaders(&_loaderThread)) {
connect(this, SIGNAL(faderOnTimer()), _fader, SLOT(onTimer()));
connect(this, SIGNAL(suppressSong()), _fader, SLOT(onSuppressSong()));
connect(this, SIGNAL(unsuppressSong()), _fader, SLOT(onUnsuppressSong()));
connect(this, SIGNAL(suppressAll()), _fader, SLOT(onSuppressAll()));
connect(this, SIGNAL(songVolumeChanged()), _fader, SLOT(onSongVolumeChanged()));
connect(this, SIGNAL(videoVolumeChanged()), _fader, SLOT(onVideoVolumeChanged()));
subscribe(Global::RefSongVolumeChanged(), [this] {
QMetaObject::invokeMethod(_fader, "onSongVolumeChanged");
});
subscribe(Global::RefVideoVolumeChanged(), [this] {
QMetaObject::invokeMethod(_fader, "onVideoVolumeChanged");
});
connect(this, SIGNAL(loaderOnStart(const AudioMsgId&,qint64)), _loader, SLOT(onStart(const AudioMsgId&,qint64)));
connect(this, SIGNAL(loaderOnCancel(const AudioMsgId&)), _loader, SLOT(onCancel(const AudioMsgId&)));
connect(&_faderThread, SIGNAL(started()), _fader, SLOT(onInit()));
connect(&_loaderThread, SIGNAL(started()), _loader, SLOT(onInit()));
connect(&_faderThread, SIGNAL(finished()), _fader, SLOT(deleteLater()));
connect(&_loaderThread, SIGNAL(finished()), _loader, SLOT(deleteLater()));
connect(_loader, SIGNAL(needToCheck()), _fader, SLOT(onTimer()));
connect(_loader, SIGNAL(error(const AudioMsgId&)), this, SLOT(onError(const AudioMsgId&)));
connect(_fader, SIGNAL(needToPreload(const AudioMsgId&)), _loader, SLOT(onLoad(const AudioMsgId&)));
@ -309,6 +309,7 @@ _loader(new AudioPlayerLoaders(&_loaderThread)) {
connect(_fader, SIGNAL(error(const AudioMsgId&)), this, SLOT(onError(const AudioMsgId&)));
connect(this, SIGNAL(stoppedOnError(const AudioMsgId&)), this, SIGNAL(updated(const AudioMsgId&)), Qt::QueuedConnection);
connect(this, SIGNAL(updated(const AudioMsgId&)), this, SLOT(onUpdated(const AudioMsgId&)));
_loaderThread.start();
_faderThread.start();
}
@ -446,6 +447,7 @@ bool AudioPlayer::fadedStop(AudioMsgId::Type type, bool *fadedStart) {
void AudioPlayer::play(const AudioMsgId &audio, int64 position) {
auto type = audio.type();
AudioMsgId stopped;
auto notLoadedYet = false;
{
QMutexLocker lock(&playerMutex);
@ -479,14 +481,11 @@ void AudioPlayer::play(const AudioMsgId &audio, int64 position) {
current->file = audio.audio()->location(true);
current->data = audio.audio()->data();
if (current->file.isEmpty() && current->data.isEmpty()) {
notLoadedYet = true;
if (audio.type() == AudioMsgId::Type::Song) {
setStoppedState(current);
if (!audio.audio()->loading()) {
DocumentOpenClickHandler::doOpen(audio.audio());
}
} else {
setStoppedState(current, AudioPlayerStoppedAtError);
onError(audio);
}
} else {
current->playbackState.position = position;
@ -498,7 +497,16 @@ void AudioPlayer::play(const AudioMsgId &audio, int64 position) {
}
}
}
if (stopped) emit updated(stopped);
if (notLoadedYet) {
if (audio.type() == AudioMsgId::Type::Song) {
DocumentOpenClickHandler::doOpen(audio.audio(), App::histItemById(audio.contextId()));
} else {
onError(audio);
}
}
if (stopped) {
emit updated(stopped);
}
}
void AudioPlayer::initFromVideo(uint64 videoPlayId, std_::unique_ptr<VideoSoundData> &&data, int64 position) {
@ -949,13 +957,15 @@ AudioCapture *audioCapture() {
return capture;
}
AudioPlayerFader::AudioPlayerFader(QThread *thread) : _timer(this), _pauseFlag(false), _paused(true),
_suppressAll(false), _suppressAllAnim(false), _suppressSong(false), _suppressSongAnim(false),
_suppressAllGain(1., 1.), _suppressSongGain(1., 1.),
_suppressAllStart(0), _suppressSongStart(0) {
AudioPlayerFader::AudioPlayerFader(QThread *thread) : QObject()
, _timer(this)
, _suppressAllGain(1., 1.)
, _suppressSongGain(1., 1.) {
moveToThread(thread);
_timer.moveToThread(thread);
_pauseTimer.moveToThread(thread);
connect(thread, SIGNAL(started()), this, SLOT(onInit()));
connect(thread, SIGNAL(finished()), this, SLOT(deleteLater()));
_timer.setSingleShot(true);
connect(&_timer, SIGNAL(timeout()), this, SLOT(onTimer()));

View file

@ -55,7 +55,7 @@ struct AudioPlaybackState {
int32 frequency = 0;
};
class AudioPlayer : public QObject, public base::Observable<AudioMsgId> {
class AudioPlayer : public QObject, public base::Observable<AudioMsgId>, private base::Subscriber {
Q_OBJECT
public:
@ -103,9 +103,6 @@ signals:
void unsuppressSong();
void suppressAll();
void songVolumeChanged();
void videoVolumeChanged();
private:
bool fadedStop(AudioMsgId::Type type, bool *fadedStart = 0);
bool updateCurrentStarted(AudioMsgId::Type type, int32 pos = -1);
@ -150,10 +147,10 @@ private:
int *currentIndex(AudioMsgId::Type type);
const int *currentIndex(AudioMsgId::Type type) const;
int _audioCurrent;
int _audioCurrent = 0;
AudioMsg _audioData[AudioSimultaneousLimit];
int _songCurrent;
int _songCurrent = 0;
AudioMsg _songData[AudioSimultaneousLimit];
AudioMsg _videoData;
@ -252,11 +249,17 @@ private:
QTimer _timer, _pauseTimer;
QMutex _pauseMutex;
bool _pauseFlag, _paused;
bool _pauseFlag = false;
bool _paused = true;
bool _suppressAll, _suppressAllAnim, _suppressSong, _suppressSongAnim, _songVolumeChanged, _videoVolumeChanged;
bool _suppressAll = false;
bool _suppressAllAnim = false;
bool _suppressSong = false;
bool _suppressSongAnim = false;
bool _songVolumeChanged, _videoVolumeChanged;
anim::fvalue _suppressAllGain, _suppressSongGain;
uint64 _suppressAllStart, _suppressSongStart;
uint64 _suppressAllStart = 0;
uint64 _suppressSongStart = 0;
};

View file

@ -27,6 +27,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
AudioPlayerLoaders::AudioPlayerLoaders(QThread *thread) : _fromVideoNotify(this, "onVideoSoundAdded") {
moveToThread(thread);
connect(thread, SIGNAL(started()), this, SLOT(onInit()));
connect(thread, SIGNAL(finished()), this, SLOT(deleteLater()));
}
void AudioPlayerLoaders::feedFromVideo(VideoSoundPart &&part) {

View file

@ -33,6 +33,127 @@ MediaPlayerButton {
cancelStroke: pixels;
}
mediaPlayerActiveFg: #54b5ed;
mediaPlayerInactiveFg: #dfebf2;
mediaPlayerButton: MediaPlayerButton {
playPosition: point(2px, 0px);
playOuter: size(17px, 15px);
pausePosition: point(1px, 1px);
pauseOuter: size(15px, 15px);
pauseStroke: 5px;
cancelPosition: point(1px, 1px);
cancelOuter: size(15px, 15px);
cancelStroke: 3px;
}
mediaPlayerButtonSize: size(25px, 30px);
mediaPlayerButtonPosition: point(5px, 10px);
mediaPlayerSkipIconPosition: point(5px, 12px);
mediaPlayerHeight: 35px;
mediaPlayerPadding: 8px;
mediaPlayerNameTop: 22px;
mediaPlayerPlayLeft: 7px;
mediaPlayerPlaySkip: 1px;
mediaPlayerPlayTop: 0px;
mediaPlayerCloseRight: 0px;
mediaPlayerName: flatLabel(labelDefFlat) {
maxHeight: 20px;
textFg: windowTextFg;
}
mediaPlayerTime: LabelSimple(defaultLabelSimple) {
textFg: windowSubTextFg;
}
mediaPlayerRepeatButton: IconButton {
width: 31px;
height: 30px;
opacity: 1.;
overOpacity: 1.;
icon: icon {
{ "player_repeat", mediaPlayerActiveFg, point(9px, 11px) }
};
iconPosition: point(0px, 0px);
downIconPosition: point(0px, 0px);
duration: 0;
}
mediaPlayerRepeatDisabledIcon: icon {
{ "player_repeat", #c8c8c8, point(9px, 11px)}
};
mediaPlayerRepeatInactiveIcon: icon {
{ "player_repeat", mediaPlayerInactiveFg, point(9px, 11px)}
};
mediaPlayerVolumeIcon0: icon {
{ "player_volume0", mediaPlayerActiveFg },
};
mediaPlayerVolumeIcon1: icon {
{ "player_volume1", mediaPlayerActiveFg },
};
mediaPlayerVolumeIcon2: icon {
{ "player_volume2", mediaPlayerActiveFg },
};
mediaPlayerVolumeIcon3: icon {
{ "player_volume3", mediaPlayerActiveFg },
};
mediaPlayerVolumeToggle: IconButton {
width: 31px;
height: 30px;
opacity: 1.;
overOpacity: 1.;
icon: mediaPlayerVolumeIcon0;
iconPosition: point(8px, 11px);
downIconPosition: point(8px, 11px);
duration: 0;
}
mediaPlayerVolumeMargin: 10px;
mediaPlayerVolumeSize: size(27px, 100px);
mediaPlayerPanelPinButton: IconButton(mediaPlayerRepeatButton) {
icon: icon {
{ "player_panel_pin", mediaPlayerActiveFg, point(9px, 11px) }
};
}
mediaPlayerNextButton: IconButton(mediaPlayerRepeatButton) {
width: 25px;
icon: icon {
{ "player_next", mediaPlayerActiveFg, mediaPlayerSkipIconPosition },
};
}
mediaPlayerNextDisabledIcon: icon {
{ "player_next", mediaPlayerInactiveFg, mediaPlayerSkipIconPosition },
};
mediaPlayerPreviousButton: IconButton(mediaPlayerNextButton) {
icon: icon {
{ "player_previous", mediaPlayerActiveFg, mediaPlayerSkipIconPosition },
};
}
mediaPlayerPreviousDisabledIcon: icon {
{ "player_previous", mediaPlayerInactiveFg, mediaPlayerSkipIconPosition },
};
mediaPlayerClose: IconButton(mediaPlayerRepeatButton) {
width: 37px;
icon: icon {
{ "player_close", #c8c8c8, point(10px, 12px) },
};
}
mediaPlayerPlayback: FilledSlider {
fullWidth: 6px;
lineWidth: 2px;
activeFg: mediaPlayerActiveFg;
inactiveFg: mediaPlayerInactiveFg;
duration: 150;
}
mediaPlayerTitleButtonSize: size(titleHeight, titleHeight);
mediaPlayerTitleButtonInner: size(25px, 25px);
mediaPlayerTitleButtonInnerBg: #49708f;
@ -48,7 +169,8 @@ mediaPlayerTitleButton: MediaPlayerButton {
cancelOuter: size(25px, 25px);
cancelStroke: 2px;
}
mediaPlayerButton: MediaPlayerButton {
mediaPlayerPanelButton: MediaPlayerButton {
playPosition: point(3px, 0px);
playOuter: size(22px, 18px);
pausePosition: point(1px, 1px);
@ -58,62 +180,40 @@ mediaPlayerButton: MediaPlayerButton {
cancelOuter: size(16px, 18px);
cancelStroke: 3px;
}
mediaPlayerPanelButtonSize: size(32px, 32px);
mediaPlayerPanelButtonPosition: point(8px, 7px);
mediaPlayerMarginLeft: 10px;
mediaPlayerMarginBottom: 10px;
mediaPlayerWidth: 344px;
mediaPlayerPanelMarginLeft: 10px;
mediaPlayerPanelMarginBottom: 10px;
mediaPlayerPanelWidth: 344px;
mediaPlayerCoverHeight: 102px;
mediaPlayerActiveFg: #54b5ed;
mediaPlayerInactiveFg: #dfebf2;
mediaPlayerButtonSize: size(32px, 32px);
mediaPlayerButtonPosition: point(8px, 7px);
mediaPlayerRepeatButton: IconButton {
width: 31px;
height: 32px;
opacity: 1.;
overOpacity: 1.;
icon: icon {
{ "player_repeat", mediaPlayerActiveFg, point(9px, 9px)}
};
iconPosition: point(0px, 0px);
downIconPosition: point(0px, 0px);
duration: 0;
}
mediaPlayerRepeatDisabledIcon: icon {
{ "player_repeat", mediaPlayerInactiveFg, point(9px, 9px)}
};
mediaPlayerPreviousButton: IconButton(mediaPlayerRepeatButton) {
mediaPlayerPanelNextButton: IconButton(mediaPlayerRepeatButton) {
width: 37px;
icon: icon {
{ "player_previous", mediaPlayerActiveFg, point(10px, 10px) },
{ "player_panel_next", mediaPlayerActiveFg, point(10px, 10px) },
};
}
mediaPlayerPreviousDisabledIcon: icon {
{ "player_previous", mediaPlayerInactiveFg, point(10px, 10px) },
mediaPlayerPanelNextDisabledIcon: icon {
{ "player_panel_next", mediaPlayerInactiveFg, point(10px, 10px) },
};
mediaPlayerNextButton: IconButton(mediaPlayerPreviousButton) {
mediaPlayerPanelPreviousButton: IconButton(mediaPlayerPanelNextButton) {
icon: icon {
{ "player_next", mediaPlayerActiveFg, point(10px, 10px) },
{ "player_panel_previous", mediaPlayerActiveFg, point(10px, 10px) },
};
}
mediaPlayerNextDisabledIcon: icon {
{ "player_next", mediaPlayerInactiveFg, point(10px, 10px) },
mediaPlayerPanelPreviousDisabledIcon: icon {
{ "player_panel_previous", mediaPlayerInactiveFg, point(10px, 10px) },
};
mediaPlayerPadding: 18px;
mediaPlayerNameTop: 24px;
mediaPlayerPlayLeft: 9px;
mediaPlayerPlaySkip: 7px;
mediaPlayerPlayTop: 58px;
mediaPlayerPlaybackTop: 32px;
mediaPlayerPlaybackPadding: 8px;
mediaPlayerPlayback: MediaSlider {
mediaPlayerPanelPadding: 18px;
mediaPlayerPanelNameTop: 24px;
mediaPlayerPanelPlayLeft: 9px;
mediaPlayerPanelPlaySkip: 7px;
mediaPlayerPanelPlayTop: 58px;
mediaPlayerPanelPlaybackTop: 32px;
mediaPlayerPanelPlaybackPadding: 8px;
mediaPlayerPanelPlayback: MediaSlider {
width: 3px;
activeFg: mediaPlayerActiveFg;
inactiveFg: mediaPlayerInactiveFg;
@ -123,41 +223,8 @@ mediaPlayerPlayback: MediaSlider {
duration: 150;
}
mediaPlayerName: flatLabel(labelDefFlat) {
maxHeight: 20px;
textFg: windowTextFg;
}
mediaPlayerTime: LabelSimple(defaultLabelSimple) {
textFg: windowSubTextFg;
}
mediaPlayerVolumeTop: 65px;
mediaPlayerVolumeRight: 51px;
mediaPlayerVolumeWidth: 86px;
mediaPlayerVolumeLength: 64px;
mediaPlayerVolumeIcon0: icon {
{ "player_volume0", mediaPlayerActiveFg },
};
mediaPlayerVolumeIcon1: icon {
{ "player_volume1", mediaPlayerActiveFg },
};
mediaPlayerVolumeIcon2: icon {
{ "player_volume2", mediaPlayerActiveFg },
};
mediaPlayerVolumeIcon3: icon {
{ "player_volume3", mediaPlayerActiveFg },
};
mediaPlayerVolumeToggle: IconButton {
width: 18px;
height: 17px;
opacity: 1.;
overOpacity: 1.;
icon: mediaPlayerVolumeIcon0;
iconPosition: point(0px, 2px);
downIconPosition: point(0px, 2px);
duration: 0;
}
mediaPlayerPanelVolumeTop: 65px;
mediaPlayerPanelVolumeSkip: 3px;
mediaPlayerPanelVolumeWidth: 64px;
mediaPlayerPanelVolumeToggleSkip: 0px;
mediaPlayerPanelVolumeToggleTop: 57px;

View file

@ -23,6 +23,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "ui/flatlabel.h"
#include "ui/widgets/label_simple.h"
#include "ui/widgets/media_slider.h"
#include "ui/buttons/icon_button.h"
#include "media/media_audio.h"
#include "media/view/media_clip_playback.h"
@ -31,7 +32,6 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "media/player/media_player_volume_controller.h"
#include "styles/style_media_player.h"
#include "styles/style_mediaview.h"
#include "shortcuts.h"
namespace Media {
namespace Player {
@ -42,7 +42,7 @@ class CoverWidget::PlayButton : public Button {
public:
PlayButton(QWidget *parent);
void setState(State state) {
void setState(PlayButtonLayout::State state) {
_layout.setState(state);
}
void finishTransform() {
@ -58,24 +58,26 @@ private:
};
CoverWidget::PlayButton::PlayButton(QWidget *parent) : Button(parent)
, _layout(st::mediaPlayerButton, [this] { update(); }) {
resize(st::mediaPlayerButtonSize);
, _layout(st::mediaPlayerPanelButton, [this] { update(); }) {
resize(st::mediaPlayerPanelButtonSize);
setCursor(style::cur_pointer);
}
void CoverWidget::PlayButton::paintEvent(QPaintEvent *e) {
Painter p(this);
p.translate(st::mediaPlayerButtonPosition.x(), st::mediaPlayerButtonPosition.y());
p.translate(st::mediaPlayerPanelButtonPosition.x(), st::mediaPlayerPanelButtonPosition.y());
_layout.paint(p, st::mediaPlayerActiveFg);
}
CoverWidget::CoverWidget(QWidget *parent) : TWidget(parent)
, _nameLabel(this, st::mediaPlayerName)
, _timeLabel(this, st::mediaPlayerTime)
, _playback(this, st::mediaPlayerPlayback)
, _playback(new Ui::MediaSlider(this, st::mediaPlayerPanelPlayback))
, _playPause(this)
, _volumeToggle(this, st::mediaPlayerVolumeToggle)
, _volumeController(this)
, _pinPlayer(this, st::mediaPlayerPanelPinButton)
, _repeatTrack(this, st::mediaPlayerRepeatButton) {
setAttribute(Qt::WA_OpaquePaintEvent);
@ -85,26 +87,34 @@ CoverWidget::CoverWidget(QWidget *parent) : TWidget(parent)
_playback->setChangeFinishedCallback([this](float64 value) {
handleSeekFinished(value);
});
_playPause->setClickedCallback([this]() {
_playPause->setClickedCallback([this] {
if (exists()) {
instance()->playPauseCancelClicked();
}
});
updateRepeatTrackIcon();
_repeatTrack->setClickedCallback([this]() {
_repeatTrack->setClickedCallback([this] {
instance()->toggleRepeat();
updateRepeatTrackIcon();
});
updateVolumeToggleIcon();
_volumeToggle->setClickedCallback([this]() {
Global::SetSongVolume((Global::SongVolume() > 0) ? 0. : Global::RememberedSongVolume());
Global::RefSongVolumeChanged().notify();
});
subscribe(Global::RefSongVolumeChanged(), [this] { updateVolumeToggleIcon(); });
if (exists()) {
subscribe(instance()->playlistChangedNotifier(), [this]() {
subscribe(instance()->repeatChangedNotifier(), [this] {
updateRepeatTrackIcon();
});
subscribe(instance()->playlistChangedNotifier(), [this] {
handlePlaylistUpdate();
});
subscribe(instance()->updatedNotifier(), [this](const UpdatedEvent &e) {
handleSongUpdate(e);
});
subscribe(instance()->songChangedNotifier(), [this]() {
subscribe(instance()->songChangedNotifier(), [this] {
handleSongChange();
});
handleSongChange();
@ -117,6 +127,10 @@ CoverWidget::CoverWidget(QWidget *parent) : TWidget(parent)
}
}
void CoverWidget::setPinCallback(PinCallback &&callback) {
_pinPlayer->setClickedCallback(std_::move(callback));
}
void CoverWidget::handleSeekProgress(float64 progress) {
if (!_lastDurationMs) return;
@ -148,15 +162,22 @@ void CoverWidget::handleSeekFinished(float64 progress) {
}
void CoverWidget::resizeEvent(QResizeEvent *e) {
_nameLabel->resizeToWidth(width() - 2 * (st::mediaPlayerPadding) - _timeLabel->width() - st::normalFont->spacew);
auto widthForName = width() - 2 * (st::mediaPlayerPanelPadding);
widthForName -= _timeLabel->width() + 2 * st::normalFont->spacew;
_nameLabel->resizeToWidth(widthForName);
updateLabelPositions();
int skip = (st::mediaPlayerPlayback.seekSize.width() / 2);
int length = (width() - 2 * st::mediaPlayerPadding + st::mediaPlayerPlayback.seekSize.width());
_playback->setGeometry(st::mediaPlayerPadding - skip, st::mediaPlayerPlaybackTop, length, 2 * st::mediaPlayerPlaybackPadding + st::mediaPlayerPlayback.width);
int skip = (st::mediaPlayerPanelPlayback.seekSize.width() / 2);
int length = (width() - 2 * st::mediaPlayerPanelPadding + st::mediaPlayerPanelPlayback.seekSize.width());
_playback->setGeometry(st::mediaPlayerPanelPadding - skip, st::mediaPlayerPanelPlaybackTop, length, 2 * st::mediaPlayerPanelPlaybackPadding + st::mediaPlayerPanelPlayback.width);
auto top = st::mediaPlayerPanelVolumeToggleTop;
auto right = st::mediaPlayerPanelPlayLeft;
_repeatTrack->moveToRight(right, top); right += _repeatTrack->width();
_pinPlayer->moveToRight(right, top); right += _pinPlayer->width() + st::mediaPlayerPanelVolumeSkip;
_volumeController->moveToRight(right, st::mediaPlayerPanelVolumeTop); right += _volumeController->width() + st::mediaPlayerPanelVolumeToggleSkip;
_volumeToggle->moveToRight(right, top);
_repeatTrack->moveToRight(st::mediaPlayerPlayLeft, st::mediaPlayerPlayTop);
_volumeController->moveToRight(st::mediaPlayerVolumeRight, st::mediaPlayerVolumeTop);
updatePlayPrevNextPositions();
}
@ -166,23 +187,24 @@ void CoverWidget::paintEvent(QPaintEvent *e) {
}
void CoverWidget::updatePlayPrevNextPositions() {
auto left = st::mediaPlayerPanelPlayLeft;
auto top = st::mediaPlayerPanelPlayTop;
if (_previousTrack) {
auto left = st::mediaPlayerPlayLeft;
_previousTrack->moveToLeft(left, st::mediaPlayerPlayTop); left += _previousTrack->width() + st::mediaPlayerPlaySkip;
_playPause->moveToLeft(left, st::mediaPlayerPlayTop); left += _playPause->width() + st::mediaPlayerPlaySkip;
_nextTrack->moveToLeft(left, st::mediaPlayerPlayTop);
_previousTrack->moveToLeft(left, top); left += _previousTrack->width() + st::mediaPlayerPanelPlaySkip;
_playPause->moveToLeft(left, top); left += _playPause->width() + st::mediaPlayerPanelPlaySkip;
_nextTrack->moveToLeft(left, top);
} else {
_playPause->moveToLeft(st::mediaPlayerPlayLeft, st::mediaPlayerPlayTop);
_playPause->moveToLeft(left, top);
}
}
void CoverWidget::updateLabelPositions() {
_nameLabel->moveToLeft(st::mediaPlayerPadding, st::mediaPlayerNameTop - st::mediaPlayerName.font->ascent);
_timeLabel->moveToRight(st::mediaPlayerPadding, st::mediaPlayerNameTop - st::mediaPlayerTime.font->ascent);
_nameLabel->moveToLeft(st::mediaPlayerPanelPadding, st::mediaPlayerPanelNameTop - st::mediaPlayerName.font->ascent);
_timeLabel->moveToRight(st::mediaPlayerPanelPadding, st::mediaPlayerPanelNameTop - st::mediaPlayerTime.font->ascent);
}
void CoverWidget::updateRepeatTrackIcon() {
_repeatTrack->setIcon(instance()->repeatEnabled() ? nullptr : &st::mediaPlayerRepeatDisabledIcon);
_repeatTrack->setIcon(instance()->repeatEnabled() ? nullptr : &st::mediaPlayerRepeatInactiveIcon);
}
void CoverWidget::handleSongUpdate(const UpdatedEvent &e) {
@ -249,7 +271,7 @@ void CoverWidget::updateTimeLabel() {
_timeLabel->setText(_time);
}
if (timeLabelWidth != _timeLabel->width()) {
_nameLabel->resizeToWidth(width() - 2 * (st::mediaPlayerPadding) - _timeLabel->width() - st::normalFont->spacew);
_nameLabel->resizeToWidth(width() - 2 * (st::mediaPlayerPanelPadding) - _timeLabel->width() - st::normalFont->spacew);
updateLabelPositions();
}
}
@ -281,17 +303,17 @@ void CoverWidget::handlePlaylistUpdate() {
createPrevNextButtons();
auto previousEnabled = (index > 0);
auto nextEnabled = (index + 1 < playlist.size());
_previousTrack->setIcon(previousEnabled ? nullptr : &st::mediaPlayerPreviousDisabledIcon);
_previousTrack->setIcon(previousEnabled ? nullptr : &st::mediaPlayerPanelPreviousDisabledIcon);
_previousTrack->setCursor(previousEnabled ? style::cur_pointer : style::cur_default);
_nextTrack->setIcon(nextEnabled ? nullptr : &st::mediaPlayerNextDisabledIcon);
_nextTrack->setIcon(nextEnabled ? nullptr : &st::mediaPlayerPanelNextDisabledIcon);
_nextTrack->setCursor(nextEnabled ? style::cur_pointer : style::cur_default);
}
}
void CoverWidget::createPrevNextButtons() {
if (!_previousTrack) {
_previousTrack.create(this, st::mediaPlayerPreviousButton);
_nextTrack.create(this, st::mediaPlayerNextButton);
_previousTrack.create(this, st::mediaPlayerPanelPreviousButton);
_nextTrack.create(this, st::mediaPlayerPanelNextButton);
_previousTrack->setClickedCallback([this]() {
if (exists()) {
instance()->previous();
@ -314,5 +336,21 @@ void CoverWidget::destroyPrevNextButtons() {
}
}
void CoverWidget::updateVolumeToggleIcon() {
auto icon = []() -> const style::icon * {
auto volume = Global::SongVolume();
if (volume > 0) {
if (volume < 1 / 3.) {
return &st::mediaPlayerVolumeIcon1;
} else if (volume < 2 / 3.) {
return &st::mediaPlayerVolumeIcon2;
}
return &st::mediaPlayerVolumeIcon3;
}
return nullptr;
};
_volumeToggle->setIcon(icon());
}
} // namespace Player
} // namespace Media

View file

@ -35,7 +35,6 @@ class Playback;
namespace Player {
class PlaybackWidget;
class VolumeController;
struct UpdatedEvent;
@ -43,6 +42,9 @@ class CoverWidget : public TWidget, private base::Subscriber {
public:
CoverWidget(QWidget *parent);
using PinCallback = base::lambda_unique<void()>;
void setPinCallback(PinCallback &&callback);
protected:
void resizeEvent(QResizeEvent *e) override;
void paintEvent(QPaintEvent *e) override;
@ -57,6 +59,8 @@ private:
void createPrevNextButtons();
void destroyPrevNextButtons();
void updateVolumeToggleIcon();
void handleSongUpdate(const UpdatedEvent &e);
void handleSongChange();
void handlePlaylistUpdate();
@ -75,7 +79,9 @@ private:
ChildWidget<Ui::IconButton> _previousTrack = { nullptr };
ChildWidget<PlayButton> _playPause;
ChildWidget<Ui::IconButton> _nextTrack = { nullptr };
ChildWidget<Ui::IconButton> _volumeToggle;
ChildWidget<VolumeController> _volumeController;
ChildWidget<Ui::IconButton> _pinPlayer;
ChildWidget<Ui::IconButton> _repeatTrack;
};

View file

@ -84,6 +84,8 @@ void Instance::handleSongUpdate(const AudioMsgId &audioId) {
void Instance::setCurrent(const AudioMsgId &audioId) {
if (_current != audioId) {
_current = audioId;
_isPlaying = false;
auto history = _history, migrated = _migrated;
auto item = _current ? App::histItemById(_current.contextId()) : nullptr;
if (item) {
@ -263,7 +265,36 @@ void Instance::emitUpdate(CheckCallback check) {
next();
}
}
_isPlaying = !(playbackState.state & AudioPlayerStoppedMask);
auto isPlaying = !(playbackState.state & AudioPlayerStoppedMask);
if (_isPlaying != isPlaying) {
_isPlaying = isPlaying;
if (_isPlaying) {
preloadNext();
}
}
}
void Instance::preloadNext() {
if (!_current) {
return;
}
auto index = _playlist.indexOf(_current.contextId());
if (index < 0) {
return;
}
auto nextIndex = index + 1;
if (nextIndex >= _playlist.size()) {
return;
}
if (auto item = App::histItemById(_playlist[nextIndex])) {
if (auto media = item->getMedia()) {
if (auto document = media->getDocument()) {
if (!document->loaded(DocumentData::FilePathResolveSaveFromDataSilent)) {
DocumentOpenClickHandler::doOpen(document, nullptr, ActionOnLoadNone);
}
}
}
}
}
} // namespace Player

View file

@ -40,11 +40,11 @@ bool exists();
class Instance;
Instance *instance();
class Widget;
struct CreatedEvent {
explicit CreatedEvent(Widget *widget) : widget(widget) {
class Panel;
struct PanelEvent {
explicit PanelEvent(Panel *panel) : panel(panel) {
}
Widget *widget;
Panel *panel;
};
struct UpdatedEvent {
UpdatedEvent(const AudioMsgId *audioId, const AudioPlaybackState *playbackState) : audioId(audioId), playbackState(playbackState) {
@ -74,6 +74,7 @@ public:
}
void toggleRepeat() {
_repeatEnabled = !_repeatEnabled;
_repeatChangedNotifier.notify();
}
bool isSeeking() const {
@ -86,9 +87,12 @@ public:
return _playlist;
}
base::Observable<CreatedEvent> &createdNotifier() {
base::Observable<PanelEvent> &createdNotifier() {
return _createdNotifier;
}
base::Observable<PanelEvent> &destroyedNotifier() {
return _destroyedNotifier;
}
base::Observable<UpdatedEvent> &updatedNotifier() {
return _updatedNotifier;
}
@ -98,6 +102,9 @@ public:
base::Observable<void> &songChangedNotifier() {
return _songChangedNotifier;
}
base::Observable<void> &repeatChangedNotifier() {
return _repeatChangedNotifier;
}
void documentLoadProgress(DocumentData *document);
@ -112,6 +119,7 @@ private:
void setCurrent(const AudioMsgId &audioId);
void rebuildPlaylist();
void moveInPlaylist(int delta);
void preloadNext();
template <typename CheckCallback>
void emitUpdate(CheckCallback check);
@ -125,10 +133,12 @@ private:
QList<FullMsgId> _playlist;
bool _isPlaying = false;
base::Observable<CreatedEvent> _createdNotifier;
base::Observable<PanelEvent> _createdNotifier;
base::Observable<PanelEvent> _destroyedNotifier;
base::Observable<UpdatedEvent> _updatedNotifier;
base::Observable<void> _playlistChangedNotifier;
base::Observable<void> _songChangedNotifier;
base::Observable<void> _repeatChangedNotifier;
};

View file

@ -0,0 +1,225 @@
/*
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-2016 John Preston, https://desktop.telegram.org
*/
#include "stdafx.h"
#include "media/player/media_player_panel.h"
#include "media/player/media_player_cover.h"
#include "media/player/media_player_list.h"
#include "media/player/media_player_instance.h"
#include "styles/style_media_player.h"
#include "mainwindow.h"
namespace Media {
namespace Player {
Panel::Panel(QWidget *parent, Layout layout) : TWidget(parent)
, _shadow(st::defaultInnerDropdown.shadow) {
if (layout == Layout::Full) {
_cover.create(this);
}
_hideTimer.setSingleShot(true);
connect(&_hideTimer, SIGNAL(timeout()), this, SLOT(onHideStart()));
_showTimer.setSingleShot(true);
connect(&_showTimer, SIGNAL(timeout()), this, SLOT(onShowStart()));
if (_scroll) {
connect(_scroll, SIGNAL(scrolled()), this, SLOT(onScroll()));
}
if (cPlatform() == dbipMac || cPlatform() == dbipMacOld) {
connect(App::wnd()->windowHandle(), SIGNAL(activeChanged()), this, SLOT(onWindowActiveChanged()));
}
hide();
resize(contentLeft() + st::mediaPlayerPanelWidth, st::mediaPlayerCoverHeight + st::mediaPlayerPanelMarginBottom);
}
bool Panel::overlaps(const QRect &globalRect) {
if (isHidden() || _a_appearance.animating()) return false;
auto marginLeft = rtl() ? 0 : contentLeft();
auto marginRight = rtl() ? contentLeft() : 0;
return rect().marginsRemoved(QMargins(marginLeft, 0, marginRight, st::mediaPlayerPanelMarginBottom)).contains(QRect(mapFromGlobal(globalRect.topLeft()), globalRect.size()));
}
void Panel::onWindowActiveChanged() {
if (!App::wnd()->windowHandle()->isActive() && !isHidden()) {
leaveEvent(nullptr);
}
}
void Panel::resizeEvent(QResizeEvent *e) {
_cover->resize(width() - contentLeft(), st::mediaPlayerCoverHeight);
_cover->moveToRight(0, 0);
if (_scroll) {
_scroll->resize(width(), height() - _cover->height());
_scroll->moveToRight(0, _cover->height());
_list->resizeToWidth(width());
}
//_scroll->setGeometry(rect().marginsRemoved(_st.padding).marginsRemoved(_st.scrollMargin));
//if (auto widget = static_cast<ScrolledWidget*>(_scroll->widget())) {
// widget->resizeToWidth(_scroll->width());
// onScroll();
//}
}
void Panel::onScroll() {
//if (auto widget = static_cast<ScrolledWidget*>(_scroll->widget())) {
// int visibleTop = _scroll->scrollTop();
// int visibleBottom = visibleTop + _scroll->height();
// widget->setVisibleTopBottom(visibleTop, visibleBottom);
//}
}
void Panel::paintEvent(QPaintEvent *e) {
Painter p(this);
if (!_cache.isNull()) {
bool animating = _a_appearance.animating(getms());
if (animating) {
p.setOpacity(_a_appearance.current(_hiding));
} else if (_hiding) {
hidingFinished();
return;
}
p.drawPixmap(0, 0, _cache);
if (!animating) {
showChildren();
_cache = QPixmap();
}
return;
}
// draw shadow
auto shadowedRect = myrtlrect(contentLeft(), 0, contentWidth(), height() - st::mediaPlayerPanelMarginBottom);
auto shadowedSides = (rtl() ? Ui::RectShadow::Side::Right : Ui::RectShadow::Side::Left) | Ui::RectShadow::Side::Bottom;
_shadow.paint(p, shadowedRect, st::defaultInnerDropdown.shadowShift, shadowedSides);
p.fillRect(shadowedRect, st::windowBg);
}
void Panel::enterEvent(QEvent *e) {
_hideTimer.stop();
if (_a_appearance.animating(getms())) {
onShowStart();
} else {
_showTimer.start(0);
}
return TWidget::enterEvent(e);
}
void Panel::leaveEvent(QEvent *e) {
_showTimer.stop();
if (_a_appearance.animating(getms())) {
onHideStart();
} else {
_hideTimer.start(300);
}
return TWidget::leaveEvent(e);
}
void Panel::otherEnter() {
_hideTimer.stop();
if (_a_appearance.animating(getms())) {
onShowStart();
} else {
_showTimer.start(300);
}
}
void Panel::otherLeave() {
_showTimer.stop();
if (_a_appearance.animating(getms())) {
onHideStart();
} else {
_hideTimer.start(0);
}
}
void Panel::setPinCallback(PinCallback &&callback) {
if (_cover) {
_cover->setPinCallback(std_::move(callback));
}
}
Panel::~Panel() {
if (exists()) {
instance()->destroyedNotifier().notify(Media::Player::PanelEvent(this), true);
}
}
void Panel::onShowStart() {
if (isHidden()) {
show();
} else if (!_hiding) {
return;
}
_hiding = false;
startAnimation();
}
void Panel::onHideStart() {
if (_hiding) return;
_hiding = true;
startAnimation();
}
void Panel::startAnimation() {
auto from = _hiding ? 1. : 0.;
auto to = _hiding ? 0. : 1.;
if (!_a_appearance.animating()) {
showChildren();
_cache = myGrab(this);
}
hideChildren();
_a_appearance.start([this] { appearanceCallback(); }, from, to, st::defaultInnerDropdown.duration);
}
void Panel::appearanceCallback() {
if (!_a_appearance.animating() && _hiding) {
_hiding = false;
hidingFinished();
} else {
update();
}
}
void Panel::hidingFinished() {
hide();
showChildren();
}
int Panel::contentLeft() const {
return st::mediaPlayerPanelMarginLeft;
}
bool Panel::eventFilter(QObject *obj, QEvent *e) {
if (e->type() == QEvent::Enter) {
otherEnter();
} else if (e->type() == QEvent::Leave) {
otherLeave();
}
return false;
}
} // namespace Player
} // namespace Media

View file

@ -0,0 +1,94 @@
/*
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-2016 John Preston, https://desktop.telegram.org
*/
#pragma once
#include "ui/effects/rect_shadow.h"
class ScrollArea;
namespace Media {
namespace Player {
class CoverWidget;
class ListWidget;
class Panel : public TWidget, private base::Subscriber {
Q_OBJECT
public:
enum class Layout {
Full,
OnlyPlaylist,
};
Panel(QWidget *parent, Layout layout);
void setLayout(Layout layout);
bool overlaps(const QRect &globalRect);
void otherEnter();
void otherLeave();
using PinCallback = base::lambda_unique<void()>;
void setPinCallback(PinCallback &&callback);
~Panel();
protected:
void resizeEvent(QResizeEvent *e) override;
void paintEvent(QPaintEvent *e) override;
void enterEvent(QEvent *e) override;
void leaveEvent(QEvent *e) override;
bool eventFilter(QObject *obj, QEvent *e) override;
private slots:
void onShowStart();
void onHideStart();
void onScroll();
void onWindowActiveChanged();
private:
void appearanceCallback();
void hidingFinished();
int contentLeft() const;
int contentWidth() const {
return width() - contentLeft();
}
void startAnimation();
bool _hiding = false;
QPixmap _cache;
FloatAnimation _a_appearance;
QTimer _hideTimer, _showTimer;
Ui::RectShadow _shadow;
ChildWidget<CoverWidget> _cover = { nullptr };
ChildWidget<ListWidget> _list = { nullptr };
ChildWidget<ScrollArea> _scroll = { nullptr };
};
} // namespace Clip
} // namespace Media

View file

@ -26,65 +26,217 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "ui/widgets/media_slider.h"
#include "styles/style_media_player.h"
#include "styles/style_widgets.h"
#include "mainwindow.h"
namespace Media {
namespace Player {
VolumeController::VolumeController(QWidget *parent) : TWidget(parent)
, _toggle(this, st::mediaPlayerVolumeToggle)
, _slider(this, st::mediaPlayerPlayback) {
_toggle->setClickedCallback([this]() {
setVolume(_slider->value() ? 0. : _rememberedVolume);
});
, _slider(this, st::mediaPlayerPanelPlayback) {
_slider->setChangeProgressCallback([this](float64 volume) {
applyVolumeChange(volume);
});
_slider->setChangeFinishedCallback([this](float64 volume) {
if (volume > 0) {
_rememberedVolume = volume;
Global::SetRememberedSongVolume(volume);
}
applyVolumeChange(volume);
});
subscribe(Global::RefSongVolumeChanged(), [this] {
if (!_slider->isChanging()) {
_slider->setValue(Global::SongVolume(), true);
}
});
auto animated = false;
setVolume(Global::SongVolume(), animated);
resize(st::mediaPlayerVolumeWidth, 2 * st::mediaPlayerPlaybackPadding + st::mediaPlayerPlayback.width);
resize(st::mediaPlayerPanelVolumeWidth, 2 * st::mediaPlayerPanelPlaybackPadding + st::mediaPlayerPanelPlayback.width);
}
void VolumeController::setIsVertical(bool vertical) {
using Direction = Ui::MediaSlider::Direction;
_slider->setDirection(vertical ? Direction::Vertical : Direction::Horizontal);
_slider->setAlwaysDisplayMarker(vertical);
}
void VolumeController::resizeEvent(QResizeEvent *e) {
_slider->resize(st::mediaPlayerVolumeLength, height());
_slider->moveToRight(0, 0);
_toggle->moveToLeft(0, (height() - _toggle->height()) / 2);
_slider->setGeometry(rect());
}
void VolumeController::setVolume(float64 volume, bool animated) {
_slider->setValue(volume, animated);
if (volume > 0) {
_rememberedVolume = volume;
Global::SetRememberedSongVolume(volume);
}
applyVolumeChange(volume);
}
void VolumeController::applyVolumeChange(float64 volume) {
if (volume > 0) {
if (volume < 1 / 3.) {
_toggle->setIcon(&st::mediaPlayerVolumeIcon1);
} else if (volume < 2 / 3.) {
_toggle->setIcon(&st::mediaPlayerVolumeIcon2);
} else {
_toggle->setIcon(&st::mediaPlayerVolumeIcon3);
}
} else {
_toggle->setIcon(nullptr);
}
if (volume != Global::SongVolume()) {
Global::SetSongVolume(volume);
if (auto player = audioPlayer()) {
emit player->songVolumeChanged();
}
Global::RefSongVolumeChanged().notify();
}
}
VolumeWidget::VolumeWidget(QWidget *parent) : TWidget(parent)
, _shadow(st::defaultInnerDropdown.shadow)
, _controller(this) {
hide();
_controller->setIsVertical(true);
_hideTimer.setSingleShot(true);
connect(&_hideTimer, SIGNAL(timeout()), this, SLOT(onHideStart()));
_showTimer.setSingleShot(true);
connect(&_showTimer, SIGNAL(timeout()), this, SLOT(onShowStart()));
if (cPlatform() == dbipMac || cPlatform() == dbipMacOld) {
connect(App::wnd()->windowHandle(), SIGNAL(activeChanged()), this, SLOT(onWindowActiveChanged()));
}
hide();
auto margin = getMargin();
resize(margin.left() + st::mediaPlayerVolumeSize.width() + margin.right(), margin.top() + st::mediaPlayerVolumeSize.height() + margin.bottom());
}
QMargins VolumeWidget::getMargin() const {
return QMargins(st::mediaPlayerVolumeMargin, st::mediaPlayerPlayback.fullWidth, st::mediaPlayerVolumeMargin, st::mediaPlayerVolumeMargin);
}
bool VolumeWidget::overlaps(const QRect &globalRect) {
if (isHidden() || _a_appearance.animating()) return false;
return rect().marginsRemoved(getMargin()).contains(QRect(mapFromGlobal(globalRect.topLeft()), globalRect.size()));
}
void VolumeWidget::onWindowActiveChanged() {
if (!App::wnd()->windowHandle()->isActive() && !isHidden()) {
leaveEvent(nullptr);
}
}
void VolumeWidget::resizeEvent(QResizeEvent *e) {
auto inner = rect().marginsRemoved(getMargin());
_controller->setGeometry(inner.x(), inner.y() - st::lineWidth, inner.width(), inner.height() + st::lineWidth - ((st::mediaPlayerVolumeSize.width() - st::mediaPlayerPanelPlayback.width) / 2));
}
void VolumeWidget::paintEvent(QPaintEvent *e) {
Painter p(this);
if (!_cache.isNull()) {
bool animating = _a_appearance.animating(getms());
if (animating) {
p.setOpacity(_a_appearance.current(_hiding));
} else if (_hiding) {
hidingFinished();
return;
}
p.drawPixmap(0, 0, _cache);
if (!animating) {
showChildren();
_cache = QPixmap();
}
return;
}
// draw shadow
auto shadowedRect = rect().marginsRemoved(getMargin());
using ShadowSide = Ui::RectShadow::Side;
auto shadowedSides = ShadowSide::Left | ShadowSide::Right | ShadowSide::Bottom;
_shadow.paint(p, shadowedRect, st::defaultInnerDropdown.shadowShift, shadowedSides);
p.fillRect(shadowedRect.x(), 0, shadowedRect.width(), shadowedRect.y() + shadowedRect.height(), st::windowBg);
}
void VolumeWidget::enterEvent(QEvent *e) {
_hideTimer.stop();
if (_a_appearance.animating(getms())) {
onShowStart();
} else {
_showTimer.start(0);
}
return TWidget::enterEvent(e);
}
void VolumeWidget::leaveEvent(QEvent *e) {
_showTimer.stop();
if (_a_appearance.animating(getms())) {
onHideStart();
} else {
_hideTimer.start(300);
}
return TWidget::leaveEvent(e);
}
void VolumeWidget::otherEnter() {
_hideTimer.stop();
if (_a_appearance.animating(getms())) {
onShowStart();
} else {
_showTimer.start(0);
}
}
void VolumeWidget::otherLeave() {
_showTimer.stop();
if (_a_appearance.animating(getms())) {
onHideStart();
} else {
_hideTimer.start(0);
}
}
void VolumeWidget::onShowStart() {
if (isHidden()) {
show();
} else if (!_hiding) {
return;
}
_hiding = false;
startAnimation();
}
void VolumeWidget::onHideStart() {
if (_hiding) return;
_hiding = true;
startAnimation();
}
void VolumeWidget::startAnimation() {
auto from = _hiding ? 1. : 0.;
auto to = _hiding ? 0. : 1.;
if (_cache.isNull()) {
showChildren();
_cache = myGrab(this);
}
hideChildren();
_a_appearance.start([this] { appearanceCallback(); }, from, to, st::defaultInnerDropdown.duration);
}
void VolumeWidget::appearanceCallback() {
if (!_a_appearance.animating() && _hiding) {
_hiding = false;
hidingFinished();
} else {
update();
}
}
void VolumeWidget::hidingFinished() {
hide();
showChildren();
_cache = QPixmap();
}
bool VolumeWidget::eventFilter(QObject *obj, QEvent *e) {
if (e->type() == QEvent::Enter) {
otherEnter();
} else if (e->type() == QEvent::Leave) {
otherLeave();
}
return false;
}
} // namespace Player
} // namespace Media

View file

@ -20,18 +20,23 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
*/
#pragma once
#include "ui/effects/rect_shadow.h"
namespace Ui {
class IconButton;
class MediaSlider;
class RectShadow;
} // namespace Ui
namespace Media {
namespace Player {
class VolumeController : public TWidget {
class VolumeController : public TWidget, private base::Subscriber {
public:
VolumeController(QWidget *parent);
void setIsVertical(bool vertical);
protected:
void resizeEvent(QResizeEvent *e) override;
@ -39,9 +44,50 @@ private:
void setVolume(float64 volume, bool animated = true);
void applyVolumeChange(float64 volume);
ChildWidget<Ui::IconButton> _toggle;
ChildWidget<Ui::MediaSlider> _slider;
float64 _rememberedVolume = Global::kDefaultVolume;
};
class VolumeWidget : public TWidget {
Q_OBJECT
public:
VolumeWidget(QWidget *parent);
bool overlaps(const QRect &globalRect);
void otherEnter();
void otherLeave();
QMargins getMargin() const;
protected:
void resizeEvent(QResizeEvent *e) override;
void paintEvent(QPaintEvent *e) override;
void enterEvent(QEvent *e) override;
void leaveEvent(QEvent *e) override;
bool eventFilter(QObject *obj, QEvent *e) override;
private slots:
void onShowStart();
void onHideStart();
void onWindowActiveChanged();
private:
void appearanceCallback();
void hidingFinished();
void startAnimation();
bool _hiding = false;
QPixmap _cache;
FloatAnimation _a_appearance;
QTimer _hideTimer, _showTimer;
Ui::RectShadow _shadow;
ChildWidget<VolumeController> _controller;
};

View file

@ -21,189 +21,371 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "stdafx.h"
#include "media/player/media_player_widget.h"
#include "media/player/media_player_cover.h"
#include "media/player/media_player_list.h"
#include "ui/flatlabel.h"
#include "ui/widgets/label_simple.h"
#include "ui/widgets/filled_slider.h"
#include "ui/widgets/shadow.h"
#include "ui/buttons/icon_button.h"
#include "media/media_audio.h"
#include "media/view/media_clip_playback.h"
#include "media/player/media_player_button.h"
#include "media/player/media_player_instance.h"
#include "media/player/media_player_volume_controller.h"
#include "styles/style_media_player.h"
#include "mainwindow.h"
#include "styles/style_mediaview.h"
namespace Media {
namespace Player {
using State = PlayButtonLayout::State;
class Widget::PlayButton : public Button {
public:
PlayButton(QWidget *parent);
void setState(PlayButtonLayout::State state) {
_layout.setState(state);
}
void finishTransform() {
_layout.finishTransform();
}
protected:
void paintEvent(QPaintEvent *e) override;
private:
PlayButtonLayout _layout;
};
Widget::PlayButton::PlayButton(QWidget *parent) : Button(parent)
, _layout(st::mediaPlayerButton, [this] { update(); }) {
resize(st::mediaPlayerButtonSize);
setCursor(style::cur_pointer);
}
void Widget::PlayButton::paintEvent(QPaintEvent *e) {
Painter p(this);
p.translate(st::mediaPlayerButtonPosition.x(), st::mediaPlayerButtonPosition.y());
_layout.paint(p, st::mediaPlayerActiveFg);
}
Widget::Widget(QWidget *parent) : TWidget(parent)
, _shadow(st::defaultInnerDropdown.shadow)
, _cover(this) {
_hideTimer.setSingleShot(true);
connect(&_hideTimer, SIGNAL(timeout()), this, SLOT(onHideStart()));
, _nameLabel(this, st::mediaPlayerName)
, _timeLabel(this, st::mediaPlayerTime)
, _playPause(this)
, _volumeToggle(this, st::mediaPlayerVolumeToggle)
, _repeatTrack(this, st::mediaPlayerRepeatButton)
, _close(this, st::mediaPlayerClose)
, _shadow(this, st::shadowColor)
, _playback(new Ui::FilledSlider(this, st::mediaPlayerPlayback)) {
setAttribute(Qt::WA_OpaquePaintEvent);
resize(st::wndMinWidth, st::mediaPlayerHeight + st::lineWidth);
_showTimer.setSingleShot(true);
connect(&_showTimer, SIGNAL(timeout()), this, SLOT(onShowStart()));
_playback->setChangeProgressCallback([this](float64 value) {
handleSeekProgress(value);
});
_playback->setChangeFinishedCallback([this](float64 value) {
handleSeekFinished(value);
});
_playPause->setClickedCallback([this] {
if (exists()) {
instance()->playPauseCancelClicked();
}
});
if (_scroll) {
connect(_scroll, SIGNAL(scrolled()), this, SLOT(onScroll()));
updateVolumeToggleIcon();
_volumeToggle->setClickedCallback([this] {
Global::SetSongVolume((Global::SongVolume() > 0) ? 0. : Global::RememberedSongVolume());
Global::RefSongVolumeChanged().notify();
});
subscribe(Global::RefSongVolumeChanged(), [this] { updateVolumeToggleIcon(); });
updateRepeatTrackIcon();
_repeatTrack->setClickedCallback([this] {
instance()->toggleRepeat();
});
if (exists()) {
subscribe(instance()->repeatChangedNotifier(), [this] {
updateRepeatTrackIcon();
});
subscribe(instance()->playlistChangedNotifier(), [this] {
handlePlaylistUpdate();
});
subscribe(instance()->updatedNotifier(), [this](const UpdatedEvent &e) {
handleSongUpdate(e);
});
subscribe(instance()->songChangedNotifier(), [this] {
handleSongChange();
});
handleSongChange();
if (auto player = audioPlayer()) {
AudioMsgId playing;
auto playbackState = player->currentState(&playing, AudioMsgId::Type::Song);
handleSongUpdate(UpdatedEvent(&playing, &playbackState));
_playPause->finishTransform();
}
}
if (cPlatform() == dbipMac || cPlatform() == dbipMacOld) {
connect(App::wnd()->windowHandle(), SIGNAL(activeChanged()), this, SLOT(onWindowActiveChanged()));
}
hide();
resize(contentLeft() + st::mediaPlayerWidth, st::mediaPlayerCoverHeight + st::mediaPlayerMarginBottom);
}
bool Widget::overlaps(const QRect &globalRect) {
if (isHidden() || _a_appearance.animating()) return false;
auto marginLeft = rtl() ? 0 : contentLeft();
auto marginRight = rtl() ? contentLeft() : 0;
return rect().marginsRemoved(QMargins(marginLeft, 0, marginRight, st::mediaPlayerMarginBottom)).contains(QRect(mapFromGlobal(globalRect.topLeft()), globalRect.size()));
void Widget::updateVolumeToggleIcon() {
auto icon = []() -> const style::icon * {
auto volume = Global::SongVolume();
if (volume > 0) {
if (volume < 1 / 3.) {
return &st::mediaPlayerVolumeIcon1;
} else if (volume < 2 / 3.) {
return &st::mediaPlayerVolumeIcon2;
}
return &st::mediaPlayerVolumeIcon3;
}
return nullptr;
};
_volumeToggle->setIcon(icon());
}
void Widget::onWindowActiveChanged() {
if (!App::wnd()->windowHandle()->isActive() && !isHidden()) {
leaveEvent(nullptr);
void Widget::setCloseCallback(CloseCallback &&callback) {
_close->setClickedCallback(std_::move(callback));
}
void Widget::setShadowGeometryToLeft(int x, int y, int w, int h) {
_shadow->setGeometryToLeft(x, y, w, h);
}
void Widget::showShadow() {
_shadow->show();
_playback->show();
}
void Widget::hideShadow() {
_shadow->hide();
_playback->hide();
}
QPoint Widget::getPositionForVolumeWidget() const {
auto x = _volumeToggle->x();
x += (_volumeToggle->width() - st::mediaPlayerVolumeSize.width()) / 2;
if (rtl()) x = width() - x - st::mediaPlayerVolumeSize.width();
return QPoint(x, height());
}
void Widget::volumeWidgetCreated(VolumeWidget *widget) {
_volumeToggle->installEventFilter(widget);
}
void Widget::handleSeekProgress(float64 progress) {
if (!_lastDurationMs) return;
auto positionMs = snap(static_cast<int64>(progress * _lastDurationMs), 0LL, _lastDurationMs);
if (_seekPositionMs != positionMs) {
_seekPositionMs = positionMs;
updateTimeLabel();
if (exists()) {
instance()->startSeeking();
}
}
}
void Widget::handleSeekFinished(float64 progress) {
if (!_lastDurationMs) return;
auto positionMs = snap(static_cast<int64>(progress * _lastDurationMs), 0LL, _lastDurationMs);
_seekPositionMs = -1;
AudioMsgId playing;
auto playbackState = audioPlayer()->currentState(&playing, AudioMsgId::Type::Song);
if (playing && playbackState.duration) {
audioPlayer()->seek(qRound(progress * playbackState.duration));
}
if (exists()) {
instance()->stopSeeking();
}
}
void Widget::resizeEvent(QResizeEvent *e) {
_cover->resize(width() - contentLeft(), st::mediaPlayerCoverHeight);
_cover->moveToRight(0, 0);
if (_scroll) {
_scroll->resize(width(), height() - _cover->height());
_scroll->moveToRight(0, _cover->height());
_list->resizeToWidth(width());
}
//_scroll->setGeometry(rect().marginsRemoved(_st.padding).marginsRemoved(_st.scrollMargin));
//if (auto widget = static_cast<ScrolledWidget*>(_scroll->widget())) {
// widget->resizeToWidth(_scroll->width());
// onScroll();
//}
}
updatePlayPrevNextPositions();
void Widget::onScroll() {
//if (auto widget = static_cast<ScrolledWidget*>(_scroll->widget())) {
// int visibleTop = _scroll->scrollTop();
// int visibleBottom = visibleTop + _scroll->height();
// widget->setVisibleTopBottom(visibleTop, visibleBottom);
//}
auto right = st::mediaPlayerCloseRight;
_close->moveToRight(right, st::mediaPlayerPlayTop); right += _close->width();
_repeatTrack->moveToRight(right, st::mediaPlayerPlayTop); right += _repeatTrack->width();
_volumeToggle->moveToRight(right, st::mediaPlayerPlayTop); right += _volumeToggle->width();
updateLabelsGeometry();
_playback->setGeometry(0, height() - st::mediaPlayerPlayback.fullWidth, width(), st::mediaPlayerPlayback.fullWidth);
}
void Widget::paintEvent(QPaintEvent *e) {
Painter p(this);
auto fill = e->rect().intersected(QRect(0, 0, width(), st::mediaPlayerHeight));
if (!fill.isEmpty()) {
p.fillRect(fill, st::windowBg);
}
}
if (!_cache.isNull()) {
bool animating = _a_appearance.animating(getms());
if (animating) {
p.setOpacity(_a_appearance.current(_hiding));
} else if (_hiding) {
hidingFinished();
return;
}
p.drawPixmap(0, 0, _cache);
if (!animating) {
showChildren();
_cache = QPixmap();
}
void Widget::updatePlayPrevNextPositions() {
auto left = st::mediaPlayerPlayLeft;
auto top = st::mediaPlayerPlayTop;
if (_previousTrack) {
_previousTrack->moveToLeft(left, top); left += _previousTrack->width() + st::mediaPlayerPlaySkip;
_playPause->moveToLeft(left, top); left += _playPause->width() + st::mediaPlayerPlaySkip;
_nextTrack->moveToLeft(left, top);
} else {
_playPause->moveToLeft(left, top);
}
}
void Widget::updateLabelsGeometry() {
auto left = st::mediaPlayerPlayLeft + _playPause->width();
if (_previousTrack) {
left += _previousTrack->width() + st::mediaPlayerPlaySkip + _nextTrack->width() + st::mediaPlayerPlaySkip;
}
left += st::mediaPlayerPadding;
auto right = st::mediaPlayerCloseRight + _close->width() + _repeatTrack->width() + _volumeToggle->width();
right += st::mediaPlayerPadding;
auto widthForName = width() - left - right;
widthForName -= _timeLabel->width() + 2 * st::normalFont->spacew;
_nameLabel->resizeToWidth(widthForName);
_nameLabel->moveToLeft(left, st::mediaPlayerNameTop - st::mediaPlayerName.font->ascent);
_timeLabel->moveToRight(right, st::mediaPlayerNameTop - st::mediaPlayerTime.font->ascent);
}
void Widget::updateRepeatTrackIcon() {
_repeatTrack->setIcon(instance()->repeatEnabled() ? nullptr : &st::mediaPlayerRepeatDisabledIcon);
}
void Widget::handleSongUpdate(const UpdatedEvent &e) {
auto &audioId = *e.audioId;
auto &playbackState = *e.playbackState;
if (!audioId || !audioId.audio()->song()) {
return;
}
// draw shadow
auto shadowedRect = myrtlrect(contentLeft(), 0, contentWidth(), height() - st::mediaPlayerMarginBottom);
auto shadowedSides = (rtl() ? Ui::RectShadow::Side::Right : Ui::RectShadow::Side::Left) | Ui::RectShadow::Side::Bottom;
_shadow.paint(p, shadowedRect, st::defaultInnerDropdown.shadowShift, shadowedSides);
p.fillRect(shadowedRect, st::windowBg);
}
_playback->updateState(*e.playbackState);
void Widget::enterEvent(QEvent *e) {
_hideTimer.stop();
if (_a_appearance.animating(getms())) {
onShowStart();
} else {
_showTimer.start(0);
auto stopped = ((playbackState.state & AudioPlayerStoppedMask) || playbackState.state == AudioPlayerFinishing);
auto showPause = !stopped && (playbackState.state == AudioPlayerPlaying || playbackState.state == AudioPlayerResuming || playbackState.state == AudioPlayerStarting);
if (exists() && instance()->isSeeking()) {
showPause = true;
}
return TWidget::enterEvent(e);
}
void Widget::leaveEvent(QEvent *e) {
_showTimer.stop();
if (_a_appearance.animating(getms())) {
onHideStart();
} else {
_hideTimer.start(300);
}
return TWidget::leaveEvent(e);
}
void Widget::otherEnter() {
_hideTimer.stop();
if (_a_appearance.animating(getms())) {
onShowStart();
} else {
_showTimer.start(300);
}
}
void Widget::otherLeave() {
_showTimer.stop();
if (_a_appearance.animating(getms())) {
onHideStart();
} else {
_hideTimer.start(0);
}
}
void Widget::onShowStart() {
if (isHidden()) {
show();
} else if (!_hiding) {
return;
}
_hiding = false;
startAnimation();
}
void Widget::onHideStart() {
if (_hiding) return;
_hiding = true;
startAnimation();
}
void Widget::startAnimation() {
auto from = _hiding ? 1. : 0.;
auto to = _hiding ? 0. : 1.;
if (!_a_appearance.animating()) {
showChildren();
_cache = myGrab(this);
}
hideChildren();
_a_appearance.start([this] {
update();
// hack, animating() call destroys lambda :(
auto that = this;
if (!_a_appearance.animating() && that->_hiding) {
that->_hiding = false;
that->hidingFinished();
auto state = [audio = audioId.audio(), showPause] {
if (audio->loading()) {
return State::Cancel;
} else if (showPause) {
return State::Pause;
}
}, from, to, st::defaultInnerDropdown.duration);
return State::Play;
};
_playPause->setState(state());
updateTimeText(audioId, playbackState);
}
void Widget::hidingFinished() {
hide();
showChildren();
}
int Widget::contentLeft() const {
return st::mediaPlayerMarginLeft;
}
bool Widget::eventFilter(QObject *obj, QEvent *e) {
if (e->type() == QEvent::Enter) {
otherEnter();
} else if (e->type() == QEvent::Leave) {
otherLeave();
void Widget::updateTimeText(const AudioMsgId &audioId, const AudioPlaybackState &playbackState) {
QString time;
qint64 position = 0, duration = 0, display = 0;
auto frequency = (playbackState.frequency ? playbackState.frequency : AudioVoiceMsgFrequency);
if (!(playbackState.state & AudioPlayerStoppedMask) && playbackState.state != AudioPlayerFinishing) {
display = position = playbackState.position;
duration = playbackState.duration;
} else {
display = playbackState.duration ? playbackState.duration : (audioId.audio()->song()->duration * frequency);
}
_lastDurationMs = (playbackState.duration * 1000LL) / frequency;
if (audioId.audio()->loading()) {
auto loaded = audioId.audio()->loadOffset();
auto loadProgress = snap(float64(loaded) / qMax(audioId.audio()->size, 1), 0., 1.);
_time = QString::number(qRound(loadProgress * 100)) + '%';
_playback->setDisabled(true);
} else {
display = display / frequency;
_time = formatDurationText(display);
_playback->setDisabled(false);
}
if (_seekPositionMs < 0) {
updateTimeLabel();
}
}
void Widget::updateTimeLabel() {
auto timeLabelWidth = _timeLabel->width();
if (_seekPositionMs >= 0) {
auto playAlready = _seekPositionMs / 1000LL;
_timeLabel->setText(formatDurationText(playAlready));
} else {
_timeLabel->setText(_time);
}
if (timeLabelWidth != _timeLabel->width()) {
updateLabelsGeometry();
}
}
void Widget::handleSongChange() {
auto &current = instance()->current();
auto song = current.audio()->song();
TextWithEntities textWithEntities;
if (song->performer.isEmpty()) {
textWithEntities.text = song->title.isEmpty() ? (current.audio()->name.isEmpty() ? qsl("Unknown Track") : current.audio()->name) : song->title;
} else {
auto title = song->title.isEmpty() ? qsl("Unknown Track") : textClean(song->title);
textWithEntities.text = song->performer + QString::fromUtf8(" \xe2\x80\x93 ") + title;
textWithEntities.entities.append({ EntityInTextBold, 0, song->performer.size(), QString() });
}
_nameLabel->setMarkedText(textWithEntities);
handlePlaylistUpdate();
}
void Widget::handlePlaylistUpdate() {
auto &current = instance()->current();
auto &playlist = instance()->playlist();
auto index = playlist.indexOf(current.contextId());
if (!current || index < 0) {
destroyPrevNextButtons();
} else {
createPrevNextButtons();
auto previousEnabled = (index > 0);
auto nextEnabled = (index + 1 < playlist.size());
_previousTrack->setIcon(previousEnabled ? nullptr : &st::mediaPlayerPreviousDisabledIcon);
_previousTrack->setCursor(previousEnabled ? style::cur_pointer : style::cur_default);
_nextTrack->setIcon(nextEnabled ? nullptr : &st::mediaPlayerNextDisabledIcon);
_nextTrack->setCursor(nextEnabled ? style::cur_pointer : style::cur_default);
}
}
void Widget::createPrevNextButtons() {
if (!_previousTrack) {
_previousTrack.create(this, st::mediaPlayerPreviousButton);
_nextTrack.create(this, st::mediaPlayerNextButton);
_previousTrack->setClickedCallback([this]() {
if (exists()) {
instance()->previous();
}
});
_nextTrack->setClickedCallback([this]() {
if (exists()) {
instance()->next();
}
});
updatePlayPrevNextPositions();
}
}
void Widget::destroyPrevNextButtons() {
if (_previousTrack) {
_previousTrack.destroy();
_nextTrack.destroy();
updatePlayPrevNextPositions();
}
return false;
}
} // namespace Player

View file

@ -20,63 +20,79 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
*/
#pragma once
#include "ui/effects/rect_shadow.h"
class AudioMsgId;
struct AudioPlaybackState;
class FlatLabel;
class ScrollArea;
namespace Ui {
class LabelSimple;
class IconButton;
class PlainShadow;
} // namespace Ui
namespace Media {
namespace Clip {
class Playback;
} // namespace Clip
namespace Player {
class CoverWidget;
class ListWidget;
class PlayButton;
class VolumeWidget;
struct UpdatedEvent;
class Widget : public TWidget, private base::Subscriber {
Q_OBJECT
public:
Widget(QWidget *parent);
bool overlaps(const QRect &globalRect);
using CloseCallback = base::lambda_unique<void()>;
void setCloseCallback(CloseCallback &&callback);
void otherEnter();
void otherLeave();
void setShadowGeometryToLeft(int x, int y, int w, int h);
void showShadow();
void hideShadow();
QPoint getPositionForVolumeWidget() const;
void volumeWidgetCreated(VolumeWidget *widget);
protected:
void resizeEvent(QResizeEvent *e) override;
void paintEvent(QPaintEvent *e) override;
void enterEvent(QEvent *e) override;
void leaveEvent(QEvent *e) override;
bool eventFilter(QObject *obj, QEvent *e) override;
private slots:
void onShowStart();
void onHideStart();
void onScroll();
void onWindowActiveChanged();
private:
void hidingFinished();
int contentLeft() const;
int contentWidth() const {
return width() - contentLeft();
}
void handleSeekProgress(float64 progress);
void handleSeekFinished(float64 progress);
void startAnimation();
void updatePlayPrevNextPositions();
void updateLabelsGeometry();
void updateRepeatTrackIcon();
void createPrevNextButtons();
void destroyPrevNextButtons();
bool _hiding = false;
void updateVolumeToggleIcon();
QPixmap _cache;
FloatAnimation _a_appearance;
void handleSongUpdate(const UpdatedEvent &e);
void handleSongChange();
void handlePlaylistUpdate();
QTimer _hideTimer, _showTimer;
void updateTimeText(const AudioMsgId &audioId, const AudioPlaybackState &playbackState);
void updateTimeLabel();
Ui::RectShadow _shadow;
ChildWidget<CoverWidget> _cover;
ChildWidget<ListWidget> _list = { nullptr };
ChildWidget<ScrollArea> _scroll = { nullptr };
int64 _seekPositionMs = -1;
int64 _lastDurationMs = 0;
QString _time;
class PlayButton;
ChildWidget<FlatLabel> _nameLabel;
ChildWidget<Ui::LabelSimple> _timeLabel;
ChildWidget<Ui::IconButton> _previousTrack = { nullptr };
ChildWidget<PlayButton> _playPause;
ChildWidget<Ui::IconButton> _nextTrack = { nullptr };
ChildWidget<Ui::IconButton> _volumeToggle;
ChildWidget<Ui::IconButton> _repeatTrack;
ChildWidget<Ui::IconButton> _close;
ChildWidget<Ui::PlainShadow> _shadow = { nullptr };
ChildWidget<Clip::Playback> _playback;
};

View file

@ -25,6 +25,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "media/view/media_clip_volume_controller.h"
#include "styles/style_mediaview.h"
#include "ui/widgets/label_simple.h"
#include "ui/widgets/media_slider.h"
#include "ui/effects/fade_animation.h"
#include "ui/buttons/icon_button.h"
#include "media/media_audio.h"
@ -34,7 +35,7 @@ namespace Clip {
Controller::Controller(QWidget *parent) : TWidget(parent)
, _playPauseResume(this, st::mediaviewPlayButton)
, _playback(this, st::mediaviewPlayback)
, _playback(new Ui::MediaSlider(this, st::mediaviewPlayback))
, _volumeController(this)
, _fullScreenToggle(this, st::mediaviewFullScreenButton)
, _playedAlready(this, st::mediaviewPlayProgressLabel)

View file

@ -22,13 +22,12 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "media/view/media_clip_playback.h"
#include "styles/style_mediaview.h"
#include "ui/widgets/media_slider.h"
#include "media/media_audio.h"
namespace Media {
namespace Clip {
Playback::Playback(QWidget *parent, const style::MediaSlider &st) : _slider(new Ui::MediaSlider(parent, st)) {
Playback::Playback(Ui::ContinuousSlider *slider) : _slider(slider) {
}
void Playback::updateState(const AudioPlaybackState &playbackState) {

View file

@ -20,32 +20,26 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
*/
#pragma once
#include "ui/widgets/media_slider.h"
#include "ui/widgets/continuous_slider.h"
struct AudioPlaybackState;
namespace style {
struct MediaSlider;
} // namespace style
namespace Ui {
class MediaSlider;
} // namespace Ui
namespace Media {
namespace Clip {
class Playback {
public:
Playback(QWidget *parent, const style::MediaSlider &st);
Playback(Ui::ContinuousSlider *slider);
void updateState(const AudioPlaybackState &playbackState);
void setFadeOpacity(float64 opacity) {
_slider->setFadeOpacity(opacity);
}
void setChangeProgressCallback(Ui::MediaSlider::Callback &&callback) {
void setChangeProgressCallback(Ui::ContinuousSlider::Callback &&callback) {
_slider->setChangeProgressCallback(std_::move(callback));
}
void setChangeFinishedCallback(Ui::MediaSlider::Callback &&callback) {
void setChangeFinishedCallback(Ui::ContinuousSlider::Callback &&callback) {
_slider->setChangeFinishedCallback(std_::move(callback));
}
void setGeometry(int x, int y, int w, int h) {
@ -68,7 +62,7 @@ public:
}
private:
Ui::MediaSlider *_slider;
Ui::ContinuousSlider *_slider;
int64 _position = 0;
int64 _duration = 0;

View file

@ -792,7 +792,7 @@ void MediaView::onDocClick() {
if (_doc->loading()) {
onSaveCancel();
} else {
DocumentOpenClickHandler::doOpen(_doc, ActionOnLoadNone);
DocumentOpenClickHandler::doOpen(_doc, nullptr, ActionOnLoadNone);
if (_doc->loading() && !_radial.animating()) {
_radial.start(_doc->progress());
}
@ -1477,7 +1477,7 @@ void MediaView::onVideoSeekFinished(int64 positionMs) {
void MediaView::onVideoVolumeChanged(float64 volume) {
Global::SetVideoVolume(volume);
emit audioPlayer()->videoVolumeChanged();
Global::RefVideoVolumeChanged().notify();
}
void MediaView::onVideoToggleFullScreen() {

View file

@ -61,23 +61,14 @@ namespace {
}
FileLoader::FileLoader(const QString &toFile, int32 size, LocationType locationType, LoadToCacheSetting toCache, LoadFromCloudSetting fromCloud, bool autoLoading)
: _prev(0)
, _next(0)
, _priority(0)
, _paused(false)
, _autoLoading(autoLoading)
, _inQueue(false)
, _complete(false)
, _localStatus(LocalNotTried)
: _autoLoading(autoLoading)
, _file(toFile)
, _fname(toFile)
, _fileIsOpen(false)
, _toCache(toCache)
, _fromCloud(fromCloud)
, _size(size)
, _type(mtpc_storage_fileUnknown)
, _locationType(locationType)
, _localTaskId(0) {
, _locationType(locationType) {
}
QByteArray FileLoader::imageFormat(const QSize &shrinkBox) const {
@ -124,7 +115,9 @@ int32 FileLoader::fullSize() const {
}
bool FileLoader::setFileName(const QString &fileName) {
if (_toCache != LoadToCacheAsWell || !_fname.isEmpty()) return fileName.isEmpty();
if (_toCache != LoadToCacheAsWell || !_fname.isEmpty()) {
return fileName.isEmpty() || (fileName == _fname);
}
_fname = fileName;
_file.setFileName(_fname);
return true;
@ -591,6 +584,7 @@ bool mtpFileLoader::tryLoadLocal() {
}
}
}
emit progress(this);
if (_localStatus != LocalNotTried) {
return _complete;

View file

@ -182,12 +182,16 @@ signals:
protected:
void readImage(const QSize &shrinkBox) const;
FileLoader *_prev, *_next;
int32 _priority;
FileLoader *_prev = nullptr;
FileLoader *_next = nullptr;
int _priority = 0;
FileLoaderQueue *_queue;
bool _paused, _autoLoading, _inQueue, _complete;
mutable LocalLoadStatus _localStatus;
bool _paused = false;
bool _autoLoading = false;
bool _inQueue = false;
bool _complete = false;
mutable LocalLoadStatus _localStatus = LocalNotTried;
virtual bool tryLoadLocal() = 0;
virtual void cancelRequests() = 0;
@ -201,7 +205,7 @@ protected:
QFile _file;
QString _fname;
bool _fileIsOpen;
bool _fileIsOpen = false;
LoadToCacheSetting _toCache;
LoadFromCloudSetting _fromCloud;
@ -212,7 +216,7 @@ protected:
mtpTypeId _type;
LocationType _locationType;
TaskId _localTaskId;
TaskId _localTaskId = 0;
mutable QByteArray _imageFormat;
mutable QPixmap _imagePixmap;

View file

@ -37,8 +37,8 @@ using AllUpdatesList = QMap<PeerData*, PeerUpdate>;
NeverFreedPointer<AllUpdatesList> AllUpdates;
void StartCallback() {
SmallUpdates.makeIfNull();
AllUpdates.makeIfNull();
SmallUpdates.createIfNull();
AllUpdates.createIfNull();
}
void FinishCallback() {
SmallUpdates.clear();
@ -63,8 +63,8 @@ void mergePeerUpdate(PeerUpdate &mergeTo, const PeerUpdate &mergeFrom) {
}
void peerUpdatedDelayed(const PeerUpdate &update) {
SmallUpdates.makeIfNull();
AllUpdates.makeIfNull();
SmallUpdates.createIfNull();
AllUpdates.createIfNull();
Global::RefHandleDelayedPeerUpdates().call();

View file

@ -31,7 +31,6 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "application.h"
#include "fileuploader.h"
#include "mainwindow.h"
#include "playerwidget.h"
#include "media/media_audio.h"
#include "media/player/media_player_instance.h"
#include "localstorage.h"

View file

@ -32,9 +32,9 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "mainwidget.h"
#include "overviewwidget.h"
#include "application.h"
#include "playerwidget.h"
#include "overview/overview_layout.h"
#include "history/history_media_types.h"
#include "media/media_audio.h"
// flick scroll taken from http://qt-project.org/doc/qt-4.8/demos-embedded-anomaly-src-flickcharm-cpp.html
@ -1903,13 +1903,8 @@ OverviewInner::~OverviewInner() {
OverviewWidget::OverviewWidget(QWidget *parent, PeerData *peer, MediaOverviewType type) : TWidget(parent)
, _scroll(this, st::historyScroll, false)
, _inner(this, &_scroll, peer, type)
, _noDropResizeIndex(false)
, _a_show(animation(this, &OverviewWidget::step_show))
, _scrollSetAfterShow(0)
, _scrollDelta(0)
, _selCount(0)
, _topShadow(this, st::shadowColor)
, _inGrab(false) {
, _topShadow(this, st::shadowColor) {
_scroll.setFocusPolicy(Qt::NoFocus);
_scroll.setWidget(&_inner);
_scroll.move(0, 0);
@ -1923,8 +1918,6 @@ OverviewWidget::OverviewWidget(QWidget *parent, PeerData *peer, MediaOverviewTyp
connect(&_scrollTimer, SIGNAL(timeout()), this, SLOT(onScrollTimer()));
_scrollTimer.setSingleShot(false);
// connect(App::main()->player(), SIGNAL(playerSongChanged(const FullMsgId&)), this, SLOT(onPlayerSongChanged(const FullMsgId&)));
switchType(type);
}
@ -1963,8 +1956,8 @@ void OverviewWidget::resizeEvent(QResizeEvent *e) {
}
_noDropResizeIndex = false;
_topShadow.resize(width() - ((!Adaptive::OneColumn() && !_inGrab) ? st::lineWidth : 0), st::lineWidth);
_topShadow.moveToLeft((!Adaptive::OneColumn() && !_inGrab) ? st::lineWidth : 0, 0);
_topShadow->resize(width() - ((!Adaptive::OneColumn() && !_inGrab) ? st::lineWidth : 0), st::lineWidth);
_topShadow->moveToLeft((!Adaptive::OneColumn() && !_inGrab) ? st::lineWidth : 0, 0);
}
void OverviewWidget::paintEvent(QPaintEvent *e) {
@ -2129,9 +2122,9 @@ void OverviewWidget::showAnimated(Window::SlideDirection direction, const Window
_cacheUnder = params.oldContentCache;
show();
_topShadow.setVisible(params.withTopBarShadow ? false : true);
_topShadow->setVisible(params.withTopBarShadow ? false : true);
_cacheOver = App::main()->grabForShowAnimation(params);
_topShadow.setVisible(params.withTopBarShadow ? true : false);
_topShadow->setVisible(params.withTopBarShadow ? true : false);
App::main()->topBar()->startAnim();
_scrollSetAfterShow = _scroll.scrollTop();
@ -2159,7 +2152,7 @@ void OverviewWidget::step_show(float64 ms, bool timer) {
float64 dt = ms / st::slideDuration;
if (dt >= 1) {
_a_show.stop();
_topShadow.show();
_topShadow->show();
a_coordUnder.finish();
a_coordOver.finish();
@ -2202,6 +2195,17 @@ void OverviewWidget::changingMsgId(HistoryItem *row, MsgId newId) {
}
}
void OverviewWidget::grapWithoutTopBarShadow() {
grabStart();
_topShadow->hide();
}
void OverviewWidget::grabFinish() {
_inGrab = false;
resizeEvent(0);
_topShadow->show();
}
void OverviewWidget::ui_repaintHistoryItem(const HistoryItem *item) {
if (peer() == item->history()->peer || migratePeer() == item->history()->peer) {
_inner.repaintItem(item);
@ -2263,15 +2267,6 @@ void OverviewWidget::onScrollTimer() {
_scroll.scrollToY(_scroll.scrollTop() + d);
}
//void OverviewWidget::onPlayerSongChanged(const FullMsgId &msgId) {
// if (type() == OverviewMusicFiles) {
// int32 top = _inner.itemTop(msgId);
// if (top > 0) {
// _scroll.scrollToY(snap(top - int(_scroll.height() - (st::msgPadding.top() + st::mediaThumbSize + st::msgPadding.bottom())) / 2, 0, _scroll.scrollTopMax()));
// }
// }
//}
void OverviewWidget::checkSelectingScroll(QPoint point) {
if (point.y() < _scroll.scrollTop()) {
_scrollDelta = point.y() - _scroll.scrollTop();

View file

@ -25,20 +25,21 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
namespace Overview {
namespace Layout {
class AbstractItem;
class ItemBase;
class Date;
} // namespace Layout
} // namespace Overview
namespace Ui {
class PlainShadow;
} // namespace Ui
class OverviewWidget;
class OverviewInner : public QWidget, public AbstractTooltipShower, public RPCSender, private base::Subscriber {
Q_OBJECT
public:
OverviewInner(OverviewWidget *overview, ScrollArea *scroll, PeerData *peer, MediaOverviewType type);
void activate();
@ -96,7 +97,6 @@ public:
~OverviewInner();
public slots:
void onUpdateSelected();
void copyContextUrl();
@ -123,7 +123,6 @@ public slots:
void onNeedSearchMessages();
private:
MsgId complexMsgId(const HistoryItem *item) const;
bool itemMigrated(MsgId msgId) const;
@ -253,7 +252,6 @@ class OverviewWidget : public TWidget, public RPCSender {
Q_OBJECT
public:
OverviewWidget(QWidget *parent, PeerData *peer, MediaOverviewType type);
void clear();
@ -309,15 +307,8 @@ public:
_inGrab = true;
resizeEvent(0);
}
void grapWithoutTopBarShadow() {
grabStart();
_topShadow.hide();
}
void grabFinish() override {
_inGrab = false;
resizeEvent(0);
_topShadow.show();
}
void grapWithoutTopBarShadow();
void grabFinish() override;
void rpcClear() override {
_inner.rpcClear();
RPCSender::rpcClear();
@ -330,12 +321,10 @@ public:
~OverviewWidget();
public slots:
void activate();
void onScroll();
void onScrollTimer();
// void onPlayerSongChanged(const FullMsgId &msgId);
void onForwardSelected();
void onDeleteSelected();
@ -344,10 +333,9 @@ public slots:
void onClearSelected();
private:
ScrollArea _scroll;
OverviewInner _inner;
bool _noDropResizeIndex;
bool _noDropResizeIndex = false;
QString _header;
@ -356,15 +344,15 @@ private:
anim::ivalue a_coordUnder, a_coordOver;
anim::fvalue a_progress;
int32 _scrollSetAfterShow;
int32 _scrollSetAfterShow = 0;
QTimer _scrollTimer;
int32 _scrollDelta;
int32 _scrollDelta = 0;
int32 _selCount;
int32 _selCount = 0;
PlainShadow _topShadow;
bool _inGrab;
ChildWidget<Ui::PlainShadow> _topShadow;
bool _inGrab = false;
};

View file

@ -214,7 +214,7 @@ using Notification = QSharedPointer<NotificationData>;
void start() {
if (LibNotifyLoaded()) {
if (Libs::notify_is_initted() || Libs::notify_init("Telegram Desktop")) {
ManagerInstance.makeIfNull();
ManagerInstance.createIfNull();
if (!ManagerInstance->init()) {
ManagerInstance.clear();
LOG(("LibNotify Error: manager failed to init!"));

View file

@ -21,7 +21,6 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "mainwindow.h"
#include "mainwidget.h"
#include "application.h"
#include "playerwidget.h"
#include "historywidget.h"
#include "localstorage.h"
#include "window/notifications_manager_default.h"

View file

@ -96,7 +96,7 @@ namespace Notifications {
void start() {
if (cPlatform() != dbipMacOld) {
ManagerInstance.makeIfNull();
ManagerInstance.createIfNull();
}
}

View file

@ -316,7 +316,7 @@ private:
void start() {
if (init()) {
ManagerInstance.makeIfNull();
ManagerInstance.createIfNull();
}
}

View file

@ -1,695 +0,0 @@
/*
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-2016 John Preston, https://desktop.telegram.org
*/
#include "stdafx.h"
#include "playerwidget.h"
#include "shortcuts.h"
#include "lang.h"
#include "boxes/addcontactbox.h"
#include "application.h"
#include "mainwindow.h"
#include "playerwidget.h"
#include "mainwidget.h"
#include "localstorage.h"
#include "media/media_audio.h"
#include "history/history_media_types.h"
PlayerWidget::PlayerWidget(QWidget *parent) : TWidget(parent)
, _a_state(animation(this, &PlayerWidget::step_state))
, _a_progress(animation(this, &PlayerWidget::step_progress)) {
resize(st::wndMinWidth, st::playerHeight);
setMouseTracking(true);
memset(_stateHovers, 0, sizeof(_stateHovers));
}
void PlayerWidget::paintEvent(QPaintEvent *e) {
Painter p(this);
QRect r(e->rect()), checkr(myrtlrect(r));
p.fillRect(r, st::playerBg->b);
if (!_playbackRect.contains(checkr)) {
if (_fullAvailable && checkr.intersects(_prevRect)) {
if (_prevAvailable) {
float64 o = _stateHovers[OverPrev];
p.setOpacity(o * 1. + (1. - o) * st::playerInactiveOpacity);
} else {
p.setOpacity(st::playerUnavailableOpacity);
}
p.drawSpriteCenterLeft(_prevRect, width(), st::playerPrev);
}
if (checkr.intersects(_playRect)) {
float64 o = _stateHovers[OverPlay];
p.setOpacity(o * 1. + (1. - o) * st::playerInactiveOpacity);
p.drawSpriteCenterLeft(_playRect, width(), (_showPause || _down == OverPlayback) ? st::playerPause : st::playerPlay);
}
if (_fullAvailable && checkr.intersects(_nextRect)) {
if (_nextAvailable) {
float64 o = _stateHovers[OverNext];
p.setOpacity(o * 1. + (1. - o) * st::playerInactiveOpacity);
} else {
p.setOpacity(st::playerUnavailableOpacity);
}
p.drawSpriteCenterLeft(_nextRect, width(), st::playerNext);
}
if (checkr.intersects(_closeRect)) {
float64 o = _stateHovers[OverClose];
p.setOpacity(o * 1. + (1. - o) * st::playerInactiveOpacity);
p.drawSpriteCenterLeft(_closeRect, width(), st::playerClose);
}
if (checkr.intersects(_volumeRect)) {
float64 o = _stateHovers[OverVolume];
p.setOpacity(o * 1. + (1. - o) * st::playerInactiveOpacity);
int32 top = _volumeRect.y() + (_volumeRect.height() - st::playerVolume.pxHeight()) / 2;
int32 left = _volumeRect.x() + (_volumeRect.width() - st::playerVolume.pxWidth()) / 2;
int32 mid = left + qRound(st::playerVolume.pxWidth() * Global::SongVolume());
int32 right = left + st::playerVolume.pxWidth();
if (rtl()) {
left = width() - left;
mid = width() - mid;
right = width() - right;
if (mid < left) {
p.drawPixmap(QRect(mid, top, left - mid, st::playerVolume.pxHeight()), App::sprite(), QRect(st::playerVolume.rect().x() + (mid - right) * cIntRetinaFactor(), st::playerVolume.rect().y(), (left - mid) * cIntRetinaFactor(), st::playerVolume.pxHeight() * cIntRetinaFactor()));
}
if (right < mid) {
p.setOpacity(st::playerUnavailableOpacity);
p.drawPixmap(QRect(right, top, mid - right, st::playerVolume.pxHeight()), App::sprite(), QRect(st::playerVolume.rect().x(), st::playerVolume.rect().y(), (mid - right) * cIntRetinaFactor(), st::playerVolume.pxHeight() * cIntRetinaFactor()));
}
} else {
if (mid > left) {
p.drawPixmap(QRect(left, top, mid - left, st::playerVolume.pxHeight()), App::sprite(), QRect(st::playerVolume.rect().x(), st::playerVolume.rect().y(), (mid - left) * cIntRetinaFactor(), st::playerVolume.pxHeight() * cIntRetinaFactor()));
}
if (right > mid) {
p.setOpacity(st::playerUnavailableOpacity);
p.drawPixmap(QRect(mid, top, right - mid, st::playerVolume.pxHeight()), App::sprite(), QRect(st::playerVolume.rect().x() + (mid - left) * cIntRetinaFactor(), st::playerVolume.rect().y(), (right - mid) * cIntRetinaFactor(), st::playerVolume.pxHeight() * cIntRetinaFactor()));
}
}
}
if (_fullAvailable && checkr.intersects(_fullRect)) {
float64 o = _stateHovers[OverFull];
p.setOpacity(o * 1. + (1. - o) * st::playerInactiveOpacity);
p.drawSpriteCenterLeft(_fullRect, width(), st::playerFull);
}
if (checkr.intersects(_repeatRect)) {
float64 o = _stateHovers[OverRepeat];
p.setOpacity(_repeat ? 1. : (o * st::playerInactiveOpacity + (1. - o) * st::playerUnavailableOpacity));
p.drawSpriteCenterLeft(_repeatRect, width(), st::playerRepeat);
}
p.setOpacity(1.);
p.setPen(st::playerTimeFg->p);
p.setFont(st::linkFont->f);
p.drawTextLeft(_infoRect.x() + _infoRect.width() - _timeWidth, _infoRect.y() + (_infoRect.height() - st::linkFont->height) / 2, width(), _time, _timeWidth);
textstyleSet(&st::playerNameStyle);
p.setPen(st::playerFg->p);
_name.drawElided(p, _infoRect.x() + (rtl() ? (_timeWidth + st::playerSkip) : 0), _infoRect.y() + (_infoRect.height() - st::linkFont->height) / 2, _infoRect.width() - _timeWidth - st::playerSkip);
textstyleRestore();
}
if (_duration) {
float64 prg = (_down == OverPlayback) ? _downProgress : a_progress.current();
int32 from = _playbackRect.x(), mid = qRound(_playbackRect.x() + prg * _playbackRect.width()), end = _playbackRect.x() + _playbackRect.width();
if (mid > from) {
p.fillRect(rtl() ? (width() - mid) : from, height() - st::playerLineHeight, mid - from, _playbackRect.height(), st::playerLineActive->b);
}
if (end > mid) {
p.fillRect(rtl() ? (width() - end) : mid, height() - st::playerLineHeight, end - mid, st::playerLineHeight, st::playerLineInactive->b);
}
if (_stateHovers[OverPlayback] > 0) {
p.setOpacity(_stateHovers[OverPlayback]);
int32 x = mid - (st::playerMoverSize.width() / 2);
p.fillRect(rtl() ? (width() - x - st::playerMoverSize.width()) : x, height() - st::playerMoverSize.height(), st::playerMoverSize.width(), st::playerMoverSize.height(), st::playerLineActive->b);
}
} else if (a_loadProgress.current() > 0) {
int32 from = _playbackRect.x(), mid = qRound(_playbackRect.x() + a_loadProgress.current() * _playbackRect.width());
if (mid > from) {
p.fillRect(rtl() ? (width() - mid) : from, height() - st::playerLineHeight, mid - from, _playbackRect.height(), st::playerLineInactive->b);
}
}
}
void PlayerWidget::mousePressEvent(QMouseEvent *e) {
QPoint pos(myrtlpoint(e->pos()));
if (e->button() == Qt::LeftButton) {
_down = OverNone;
if (_song && _over == OverPlay) {
playPausePressed();
return;
} else if (_over == OverPrev) {
prevPressed();
} else if (_over == OverNext) {
nextPressed();
} else if (_over == OverClose) {
_down = OverClose;
} else if (_over == OverVolume) {
_down = OverVolume;
_downCoord = pos.x() - _volumeRect.x();
Global::SetSongVolume(snap((_downCoord - ((_volumeRect.width() - st::playerVolume.pxWidth()) / 2)) / float64(st::playerVolume.pxWidth()), 0., 1.));
emit audioPlayer()->songVolumeChanged();
rtlupdate(_volumeRect);
} else if (_over == OverPlayback) {
AudioMsgId playing;
auto playbackState = audioPlayer()->currentState(&playing, AudioMsgId::Type::Song);
if (playing == _song && playbackState.duration) {
if (playbackState.state == AudioPlayerPlaying || playbackState.state == AudioPlayerStarting || playbackState.state == AudioPlayerResuming) {
audioPlayer()->pauseresume(AudioMsgId::Type::Song);
}
_down = OverPlayback;
_downProgress = snap((pos.x() - _playbackRect.x()) / float64(_playbackRect.width()), 0., 1.);
_downDuration = playbackState.duration;
_downFrequency = (playbackState.frequency ? playbackState.frequency : AudioVoiceMsgFrequency);
rtlupdate(_playbackRect);
updateDownTime();
}
} else if (_over == OverFull && _song) {
if (HistoryItem *item = App::histItemById(_song.contextId())) {
App::main()->showMediaOverview(item->history()->peer, OverviewMusicFiles);
}
} else if (_over == OverRepeat) {
_repeat = !_repeat;
updateOverRect(OverRepeat);
}
}
}
void PlayerWidget::updateDownTime() {
QString time = formatDurationText(qRound(_downDuration * _downProgress) / _downFrequency);
if (time != _time) {
_time = time;
_timeWidth = st::linkFont->width(_time);
rtlupdate(_infoRect);
}
}
void PlayerWidget::updateOverState(OverState newState) {
bool result = true;
if (_over != newState) {
updateOverRect(_over);
updateOverRect(newState);
if (_over != OverNone) {
_stateAnimations.remove(_over);
_stateAnimations[-_over] = getms() - ((1. - _stateHovers[_over]) * st::playerDuration);
if (!_a_state.animating()) _a_state.start();
} else {
result = false;
}
_over = newState;
if (newState != OverNone) {
_stateAnimations.remove(-_over);
_stateAnimations[_over] = getms() - (_stateHovers[_over] * st::playerDuration);
if (!_a_state.animating()) _a_state.start();
setCursor(style::cur_pointer);
} else {
setCursor(style::cur_default);
}
}
}
void PlayerWidget::updateOverRect(OverState state) {
switch (state) {
case OverPrev: rtlupdate(_prevRect); break;
case OverPlay: rtlupdate(_playRect); break;
case OverNext: rtlupdate(_nextRect); break;
case OverClose: rtlupdate(_closeRect); break;
case OverVolume: rtlupdate(_volumeRect); break;
case OverFull: rtlupdate(_fullRect); break;
case OverRepeat: rtlupdate(_repeatRect); break;
case OverPlayback: rtlupdate(_playbackRect); break;
}
}
void PlayerWidget::updateControls() {
_fullAvailable = (_index >= 0);
History *history = _msgmigrated ? _migrated : _history;
_prevAvailable = _fullAvailable && ((_index > 0) || (_index == 0 && _migrated && !_msgmigrated && !_migrated->overview[OverviewMusicFiles].isEmpty()));
_nextAvailable = _fullAvailable && ((_index < history->overview[OverviewMusicFiles].size() - 1) || (_msgmigrated && _index == _migrated->overview[OverviewMusicFiles].size() - 1 && _history->overviewLoaded(OverviewMusicFiles) && _history->overviewCount(OverviewMusicFiles) > 0));
resizeEvent(0);
update();
if (_index >= 0 && _index < MediaOverviewStartPerPage) {
if (!_history->overviewLoaded(OverviewMusicFiles) || (_migrated && !_migrated->overviewLoaded(OverviewMusicFiles))) {
if (App::main()) {
if (_msgmigrated || (_migrated && _index == 0 && _history->overviewLoaded(OverviewMusicFiles))) {
App::main()->loadMediaBack(_migrated->peer, OverviewMusicFiles);
} else {
App::main()->loadMediaBack(_history->peer, OverviewMusicFiles);
if (_migrated && _index == 0 && _migrated->overview[OverviewMusicFiles].isEmpty() && !_migrated->overviewLoaded(OverviewMusicFiles)) {
App::main()->loadMediaBack(_migrated->peer, OverviewMusicFiles);
}
}
if (_msgmigrated && !_history->overviewCountLoaded(OverviewMusicFiles)) {
App::main()->preloadOverview(_history->peer, OverviewMusicFiles);
}
}
}
}
}
void PlayerWidget::findCurrent() {
_index = -1;
if (!_history || !_song.contextId().msg) return;
const History::MediaOverview *o = &(_msgmigrated ? _migrated : _history)->overview[OverviewMusicFiles];
if ((_msgmigrated ? _migrated : _history)->channelId() == _song.contextId().channel) {
for (int i = 0, l = o->size(); i < l; ++i) {
if (o->at(i) == _song.contextId().msg) {
_index = i;
break;
}
}
}
preloadNext();
}
void PlayerWidget::preloadNext() {
if (_index < 0) return;
History *history = _msgmigrated ? _migrated : _history;
const History::MediaOverview *o = &history->overview[OverviewMusicFiles];
HistoryItem *next = 0;
if (_index < o->size() - 1) {
next = App::histItemById(history->channelId(), o->at(_index + 1));
} else if (_msgmigrated && _index == o->size() - 1 && _history->overviewLoaded(OverviewMusicFiles) && _history->overviewCount(OverviewMusicFiles) > 0) {
next = App::histItemById(_history->channelId(), _history->overview[OverviewMusicFiles].at(0));
} else if (_msgmigrated && _index == o->size() - 1 && !_history->overviewCountLoaded(OverviewMusicFiles)) {
if (App::main()) App::main()->preloadOverview(_history->peer, OverviewMusicFiles);
}
if (next) {
if (HistoryDocument *document = static_cast<HistoryDocument*>(next->getMedia())) {
DocumentData *d = document->getDocument();
if (!d->loaded(DocumentData::FilePathResolveSaveFromDataSilent)) {
DocumentOpenClickHandler::doOpen(d, ActionOnLoadNone);
}
}
}
}
void PlayerWidget::startPlay(const FullMsgId &msgId) {
if (HistoryItem *item = App::histItemById(msgId)) {
if (HistoryDocument *doc = static_cast<HistoryDocument*>(item->getMedia())) {
audioPlayer()->play(AudioMsgId(doc->getDocument(), item->fullId()));
updateState();
}
}
}
void PlayerWidget::clearSelection() {
for (StateAnimations::const_iterator i = _stateAnimations.cbegin(); i != _stateAnimations.cend(); ++i) {
_stateHovers[qAbs(i.key())] = 0;
}
_stateAnimations.clear();
}
void PlayerWidget::mediaOverviewUpdated(PeerData *peer, MediaOverviewType type) {
if (_history && (_history->peer == peer || (_migrated && _migrated->peer == peer)) && type == OverviewMusicFiles) {
_index = -1;
History *history = _msgmigrated ? _migrated : _history;
if (history->channelId() == _song.contextId().channel && _song.contextId().msg) {
for (int i = 0, l = history->overview[OverviewMusicFiles].size(); i < l; ++i) {
if (history->overview[OverviewMusicFiles].at(i) == _song.contextId().msg) {
_index = i;
preloadNext();
break;
}
}
}
updateControls();
}
}
bool PlayerWidget::seekingSong(const AudioMsgId &song) const {
return (_down == OverPlayback) && (song == _song);
}
void PlayerWidget::openPlayer() {
_playerOpened = true;
Shortcuts::enableMediaShortcuts();
}
bool PlayerWidget::isOpened() const {
return _playerOpened;
}
void PlayerWidget::closePlayer() {
_playerOpened = false;
Shortcuts::disableMediaShortcuts();
}
void PlayerWidget::showPlayer() {
TWidget::show();
}
void PlayerWidget::hidePlayer() {
clearSelection();
TWidget::hide();
}
void PlayerWidget::step_state(uint64 ms, bool timer) {
for (StateAnimations::iterator i = _stateAnimations.begin(); i != _stateAnimations.cend();) {
int32 over = qAbs(i.key());
updateOverRect(OverState(over));
float64 dt = float64(ms - i.value()) / st::playerDuration;
if (dt >= 1) {
_stateHovers[over] = (i.key() > 0) ? 1 : 0;
i = _stateAnimations.erase(i);
} else {
_stateHovers[over] = (i.key() > 0) ? dt : (1 - dt);
++i;
}
}
if (_stateAnimations.isEmpty()) {
_a_state.stop();
}
}
void PlayerWidget::mouseMoveEvent(QMouseEvent *e) {
_lastMousePos = e->globalPos();
updateSelected();
}
void PlayerWidget::leaveEvent(QEvent *e) {
_lastMousePos = QCursor::pos();
updateSelected();
}
void PlayerWidget::updateSelected() {
QPoint pos(myrtlpoint(mapFromGlobal(_lastMousePos)));
if (_down == OverVolume) {
int32 delta = (pos.x() - _volumeRect.x()) - _downCoord;
float64 startFrom = snap((_downCoord - ((_volumeRect.width() - st::playerVolume.pxWidth()) / 2)) / float64(st::playerVolume.pxWidth()), 0., 1.);
float64 add = delta / float64(4 * st::playerVolume.pxWidth()), result = snap(startFrom + add, 0., 1.);
if (result != Global::SongVolume()) {
Global::SetSongVolume(result);
emit audioPlayer()->songVolumeChanged();
rtlupdate(_volumeRect);
}
} else if (_down == OverPlayback) {
_downProgress = snap((pos.x() - _playbackRect.x()) / float64(_playbackRect.width()), 0., 1.);
rtlupdate(_playbackRect);
updateDownTime();
} else if (_down == OverNone) {
bool inInfo = ((pos.x() >= _infoRect.x()) && (pos.x() < _fullRect.x() + _fullRect.width()) && (pos.y() >= _playRect.y()) && (pos.y() <= _playRect.y() + _playRect.height()));
if (_prevAvailable && _prevRect.contains(pos)) {
updateOverState(OverPrev);
} else if (_nextAvailable && _nextRect.contains(pos)) {
updateOverState(OverNext);
} else if (_playRect.contains(pos)) {
updateOverState(OverPlay);
} else if (_closeRect.contains(pos)) {
updateOverState(OverClose);
} else if (_volumeRect.contains(pos)) {
updateOverState(OverVolume);
} else if (_repeatRect.contains(pos)) {
updateOverState(OverRepeat);
} else if (_duration && _playbackRect.contains(pos)) {
updateOverState(OverPlayback);
} else if (_fullAvailable && inInfo) {
updateOverState(OverFull);
} else if (_over != OverNone) {
updateOverState(OverNone);
}
}
}
void PlayerWidget::mouseReleaseEvent(QMouseEvent *e) {
if (_down == OverVolume) {
mouseMoveEvent(e);
Local::writeUserSettings();
} else if (_down == OverPlayback) {
mouseMoveEvent(e);
AudioMsgId playing;
auto playbackState = audioPlayer()->currentState(&playing, AudioMsgId::Type::Song);
if (playing == _song && playbackState.duration) {
_downDuration = playbackState.duration;
audioPlayer()->seek(qRound(_downProgress * _downDuration));
_showPause = true;
a_progress = anim::fvalue(_downProgress, _downProgress);
_a_progress.stop();
}
update();
} else if (_down == OverClose && _over == OverClose) {
closePressed();
}
_down = OverNone;
}
void PlayerWidget::playPressed() {
if (!_song || isHidden()) return;
AudioMsgId playing;
auto playbackState = audioPlayer()->currentState(&playing, AudioMsgId::Type::Song);
if (playing == _song && !(playbackState.state & AudioPlayerStoppedMask)) {
if (playbackState.state == AudioPlayerPausing || playbackState.state == AudioPlayerPaused || playbackState.state == AudioPlayerPausedAtEnd) {
audioPlayer()->pauseresume(AudioMsgId::Type::Song);
}
} else {
audioPlayer()->play(_song);
audioPlayer()->notify(_song);
}
}
void PlayerWidget::pausePressed() {
if (!_song || isHidden()) return;
AudioMsgId playing;
auto playbackState = audioPlayer()->currentState(&playing, AudioMsgId::Type::Song);
if (playing == _song && !(playbackState.state & AudioPlayerStoppedMask)) {
if (playbackState.state == AudioPlayerStarting || playbackState.state == AudioPlayerResuming || playbackState.state == AudioPlayerPlaying || playbackState.state == AudioPlayerFinishing) {
audioPlayer()->pauseresume(AudioMsgId::Type::Song);
}
}
}
void PlayerWidget::playPausePressed() {
if (!_song || isHidden()) return;
AudioMsgId playing;
auto playbackState = audioPlayer()->currentState(&playing, AudioMsgId::Type::Song);
if (playing == _song && !(playbackState.state & AudioPlayerStoppedMask)) {
audioPlayer()->pauseresume(AudioMsgId::Type::Song);
} else {
audioPlayer()->play(_song);
audioPlayer()->notify(_song);
}
}
void PlayerWidget::prevPressed() {
if (isHidden()) return;
auto history = _msgmigrated ? _migrated : _history;
auto o = history ? &history->overview[OverviewMusicFiles] : nullptr;
if (audioPlayer() && o && _index > 0 && _index <= o->size() && !o->isEmpty()) {
startPlay(FullMsgId(history->channelId(), o->at(_index - 1)));
} else if (!_index && _history && _migrated && !_msgmigrated) {
o = &_migrated->overview[OverviewMusicFiles];
if (!o->isEmpty()) {
startPlay(FullMsgId(_migrated->channelId(), o->at(o->size() - 1)));
}
}
}
void PlayerWidget::nextPressed() {
if (isHidden()) return;
auto history = _msgmigrated ? _migrated : _history;
auto o = history ? &history->overview[OverviewMusicFiles] : nullptr;
if (audioPlayer() && o && _index >= 0 && _index < o->size() - 1) {
startPlay(FullMsgId(history->channelId(), o->at(_index + 1)));
} else if (o && (_index == o->size() - 1) && _msgmigrated && _history->overviewLoaded(OverviewMusicFiles)) {
o = &_history->overview[OverviewMusicFiles];
if (!o->isEmpty()) {
startPlay(FullMsgId(_history->channelId(), o->at(0)));
}
}
}
void PlayerWidget::stopPressed() {
if (!_song || isHidden()) return;
audioPlayer()->stop(AudioMsgId::Type::Song);
}
void PlayerWidget::closePressed() {
stopPressed();
if (App::main()) App::main()->closePlayer();
}
void PlayerWidget::resizeEvent(QResizeEvent *e) {
int32 availh = (height() - st::playerLineHeight);
int32 ch = st::playerPlay.pxHeight() + st::playerSkip, ct = (availh - ch) / 2;
_playbackRect = QRect(Adaptive::OneColumn() ? 0 : st::lineWidth, height() - st::playerMoverSize.height(), width() - (Adaptive::OneColumn() ? 0 : st::lineWidth), st::playerMoverSize.height());
_prevRect = _fullAvailable ? QRect(st::playerSkip / 2, ct, st::playerPrev.pxWidth() + st::playerSkip, ch) : QRect();
_playRect = QRect(_fullAvailable ? (_prevRect.x() + _prevRect.width()) : (st::playerSkip / 2), ct, st::playerPlay.pxWidth() + st::playerSkip, ch);
_nextRect = _fullAvailable ? QRect(_playRect.x() + _playRect.width(), ct, st::playerNext.pxWidth() + st::playerSkip, ch) : QRect();
_closeRect = QRect(width() - st::playerSkip / 2 - st::playerClose.pxWidth() - st::playerSkip, ct, st::playerClose.pxWidth() + st::playerSkip, ch);
_volumeRect = QRect(_closeRect.x() - st::playerVolume.pxWidth() - st::playerSkip, ct, st::playerVolume.pxWidth() + st::playerSkip, ch);
_repeatRect = QRect(_volumeRect.x() - st::playerRepeat.pxWidth() - st::playerSkip, ct, st::playerRepeat.pxWidth() + st::playerSkip, ch);
_fullRect = _fullAvailable ? QRect(_repeatRect.x() - st::playerFull.pxWidth() - st::playerSkip, ct, st::playerFull.pxWidth() + st::playerSkip, ch) : QRect();
int32 infoLeft = (_fullAvailable ? (_nextRect.x() + _nextRect.width()) : (_playRect.x() + _playRect.width()));
_infoRect = QRect(infoLeft + st::playerSkip / 2, 0, (_fullAvailable ? _fullRect.x() : _repeatRect.x()) - infoLeft - st::playerSkip, availh);
update();
}
void PlayerWidget::step_progress(float64 ms, bool timer) {
float64 dt = ms / (2 * AudioVoiceMsgUpdateView);
if (_duration && dt >= 1) {
_a_progress.stop();
a_progress.finish();
a_loadProgress.finish();
} else {
a_progress.update(qMin(dt, 1.), anim::linear);
a_loadProgress.update(1. - (st::radialDuration / (st::radialDuration + ms)), anim::linear);
}
if (timer) rtlupdate(_playbackRect);
}
void PlayerWidget::updateState() {
updateState(AudioMsgId(), AudioPlaybackState());
}
void PlayerWidget::updateState(AudioMsgId playing, AudioPlaybackState playbackState) {
if (!playing) {
playbackState = audioPlayer()->currentState(&playing, AudioMsgId::Type::Song);
}
bool songChanged = false;
if (playing && _song != playing) {
songChanged = true;
_song = playing;
if (HistoryItem *item = App::histItemById(_song.contextId())) {
_history = item->history();
if (_history->peer->migrateFrom()) {
_migrated = App::history(_history->peer->migrateFrom()->id);
_msgmigrated = false;
} else if (_history->peer->migrateTo()) {
_migrated = _history;
_history = App::history(_migrated->peer->migrateTo()->id);
_msgmigrated = true;
}
findCurrent();
} else {
_history = nullptr;
_msgmigrated = false;
_index = -1;
}
auto song = _song.audio()->song();
if (song->performer.isEmpty()) {
_name.setText(st::linkFont, song->title.isEmpty() ? (_song.audio()->name.isEmpty() ? qsl("Unknown Track") : _song.audio()->name) : song->title, _textNameOptions);
} else {
TextCustomTagsMap custom;
custom.insert(QChar('c'), qMakePair(textcmdStartLink(1), textcmdStopLink()));
_name.setRichText(st::linkFont, QString::fromUtf8("[c]%1[/c] \xe2\x80\x93 %2").arg(textRichPrepare(song->performer)).arg(song->title.isEmpty() ? qsl("Unknown Track") : textRichPrepare(song->title)), _textNameOptions, custom);
}
updateControls();
}
qint64 position = 0, duration = 0, display = 0;
if (playing == _song) {
if (!(playbackState.state & AudioPlayerStoppedMask) && playbackState.state != AudioPlayerFinishing) {
display = position = playbackState.position;
duration = playbackState.duration;
} else {
display = playbackState.duration;
}
display = display / (playbackState.frequency ? playbackState.frequency : AudioVoiceMsgFrequency);
} else if (_song) {
display = _song.audio()->song()->duration;
}
bool showPause = false, stopped = ((playbackState.state & AudioPlayerStoppedMask) || playbackState.state == AudioPlayerFinishing);
bool wasPlaying = (_duration != 0);
if (!stopped) {
showPause = (playbackState.state == AudioPlayerPlaying || playbackState.state == AudioPlayerResuming || playbackState.state == AudioPlayerStarting);
}
QString time;
float64 progress = 0.;
int32 loaded;
float64 loadProgress = 1.;
if (duration || !_song || !_song.audio() || !_song.audio()->loading()) {
time = (_down == OverPlayback) ? _time : formatDurationText(display);
progress = duration ? snap(float64(position) / duration, 0., 1.) : 0.;
loaded = duration ? _song.audio()->size : 0;
} else {
loaded = _song.audio()->loading() ? _song.audio()->loadOffset() : 0;
time = formatDownloadText(loaded, _song.audio()->size);
loadProgress = snap(float64(loaded) / qMax(_song.audio()->size, 1), 0., 1.);
}
if (time != _time || showPause != _showPause) {
if (_time != time) {
_time = time;
_timeWidth = st::linkFont->width(_time);
}
_showPause = showPause;
if (duration != _duration || position != _position || loaded != _loaded) {
if (!songChanged && ((!stopped && duration && _duration) || (!duration && _loaded != loaded))) {
a_progress.start(progress);
a_loadProgress.start(loadProgress);
_a_progress.start();
} else {
a_progress = anim::fvalue(progress, progress);
a_loadProgress = anim::fvalue(loadProgress, loadProgress);
_a_progress.stop();
}
_position = position;
_duration = duration;
_loaded = loaded;
}
update();
} else if (duration != _duration || position != _position || loaded != _loaded) {
if (!songChanged && ((!stopped && duration && _duration) || (!duration && _loaded != loaded))) {
a_progress.start(progress);
a_loadProgress.start(loadProgress);
_a_progress.start();
} else {
a_progress = anim::fvalue(progress, progress);
a_loadProgress = anim::fvalue(loadProgress, loadProgress);
_a_progress.stop();
}
_position = position;
_duration = duration;
_loaded = loaded;
}
if (wasPlaying && playbackState.state == AudioPlayerStoppedAtEnd) {
if (_repeat) {
if (_song.audio()) {
// audioPlayer()->play(_song);
// updateState();
}
} else {
// nextPressed();
}
}
if (songChanged) {
emit playerSongChanged(_song.contextId());
}
}

View file

@ -1,138 +0,0 @@
/*
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-2016 John Preston, https://desktop.telegram.org
*/
#pragma once
#include "media/media_audio.h"
class PlayerWidget : public TWidget {
Q_OBJECT
public:
PlayerWidget(QWidget *parent);
void paintEvent(QPaintEvent *e);
void mousePressEvent(QMouseEvent *e);
void mouseMoveEvent(QMouseEvent *e);
void leaveEvent(QEvent *e);
void mouseReleaseEvent(QMouseEvent *e);
void resizeEvent(QResizeEvent *e);
void playPressed();
void pausePressed();
void playPausePressed();
void prevPressed();
void nextPressed();
void stopPressed();
void closePressed();
void step_progress(float64 ms, bool timer);
void step_state(uint64 ms, bool timer);
void updateState(AudioMsgId playing, AudioPlaybackState playbackState);
void updateState();
void clearSelection();
void mediaOverviewUpdated(PeerData *peer, MediaOverviewType type);
bool seekingSong(const AudioMsgId &song) const;
void openPlayer();
bool isOpened() const;
void closePlayer();
void showPlayer();
void hidePlayer();
signals:
void playerSongChanged(const FullMsgId &msgId);
private:
// Use startPlayer()/stopPlayer() or showPlayer()/hidePlayer() instead.
void show();
void hide();
enum OverState {
OverNone = 0,
OverPrev,
OverPlay,
OverNext,
OverClose,
OverVolume,
OverFull,
OverRepeat,
OverPlayback,
OverStateCount
};
void updateDownTime();
void updateOverState(OverState newState);
void updateOverRect(OverState state);
void updateControls();
void findCurrent();
void preloadNext();
void startPlay(const FullMsgId &msgId);
QPoint _lastMousePos;
void updateSelected();
bool _playerOpened = false;
bool _prevAvailable = false;
bool _nextAvailable = false;
bool _fullAvailable = false;
OverState _over = OverNone;
OverState _down = OverNone;
int32 _downCoord = 0;
int64 _downDuration;
int32 _downFrequency = AudioVoiceMsgFrequency;
float64 _downProgress = 0.;
float64 _stateHovers[OverStateCount];
typedef QMap<int32, uint64> StateAnimations;
StateAnimations _stateAnimations;
Animation _a_state;
AudioMsgId _song;
bool _msgmigrated = false;
int32 _index = -1;
History *_migrated = nullptr;
History *_history = nullptr;
QRect _playRect, _prevRect, _nextRect, _playbackRect;
QRect _closeRect, _volumeRect, _fullRect, _repeatRect, _infoRect;
int32 _timeWidth = 0;
bool _repeat = false;
QString _time;
Text _name;
bool _showPause = false;
int64 _position = 0;
int64 _duration = 0;
int32 _loaded = 0;
anim::fvalue a_progress = { 0., 0. };
anim::fvalue a_loadProgress = { 0., 0. };
Animation _a_progress;
};

View file

@ -38,7 +38,7 @@ Widget::Widget(QWidget *parent, PeerData *peer) : Window::SectionWidget(parent)
_fixedBar->resizeToWidth(width());
_fixedBar->show();
_fixedBarShadow->setMode(ToggleableShadow::Mode::HiddenFast);
_fixedBarShadow->setMode(Ui::ToggleableShadow::Mode::HiddenFast);
_fixedBarShadow->raise();
updateAdaptiveLayout();
subscribe(Adaptive::Changed(), [this]() { updateAdaptiveLayout(); });
@ -85,7 +85,7 @@ bool Widget::showInternal(const Window::SectionMemento *memento) {
void Widget::setInternalState(const SectionMemento *memento) {
myEnsureResized(this);
_scroll->scrollToY(memento->_scrollTop);
_fixedBarShadow->setMode(memento->_scrollTop > 0 ? ToggleableShadow::Mode::ShownFast : ToggleableShadow::Mode::HiddenFast);
_fixedBarShadow->setMode(memento->_scrollTop > 0 ? Ui::ToggleableShadow::Mode::ShownFast : Ui::ToggleableShadow::Mode::HiddenFast);
}
std_::unique_ptr<Window::SectionMemento> Widget::createMemento() const {
@ -116,14 +116,14 @@ void Widget::resizeEvent(QResizeEvent *e) {
}
int scrollTop = _scroll->scrollTop();
_inner->setVisibleTopBottom(scrollTop, scrollTop + _scroll->height());
_fixedBarShadow->setMode((scrollTop > 0) ? ToggleableShadow::Mode::Shown : ToggleableShadow::Mode::Hidden);
_fixedBarShadow->setMode((scrollTop > 0) ? Ui::ToggleableShadow::Mode::Shown : Ui::ToggleableShadow::Mode::Hidden);
}
}
void Widget::onScroll() {
int scrollTop = _scroll->scrollTop();
_inner->setVisibleTopBottom(scrollTop, scrollTop + _scroll->height());
_fixedBarShadow->setMode((scrollTop > 0) ? ToggleableShadow::Mode::Shown : ToggleableShadow::Mode::Hidden);
_fixedBarShadow->setMode((scrollTop > 0) ? Ui::ToggleableShadow::Mode::Shown : Ui::ToggleableShadow::Mode::Hidden);
}
void Widget::showAnimatedHook() {

View file

@ -21,6 +21,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#pragma once
#include "window/section_widget.h"
#include "ui/widgets/shadow.h"
class ScrollArea;
@ -70,7 +71,7 @@ private:
ChildWidget<ScrollArea> _scroll;
ChildWidget<InnerWidget> _inner;
ChildWidget<FixedBar> _fixedBar;
ChildWidget<ToggleableShadow> _fixedBarShadow;
ChildWidget<Ui::ToggleableShadow> _fixedBarShadow;
};

View file

@ -21,7 +21,6 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "mainwindow.h"
#include "mainwidget.h"
#include "application.h"
#include "playerwidget.h"
#include "localstorage.h"
#include "media/player/media_player_instance.h"
#include "platform/mac/mac_utilities.h"

View file

@ -24,6 +24,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "settings/settings_inner_widget.h"
#include "settings/settings_fixed_bar.h"
#include "styles/style_settings.h"
#include "ui/widgets/shadow.h"
#include "ui/scrollarea.h"
#include "mainwindow.h"
#include "mainwidget.h"

View file

@ -22,6 +22,10 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "layerwidget.h"
namespace Ui {
class PlainShadow;
} // namespace Ui
namespace Settings {
class InnerWidget;
@ -50,7 +54,7 @@ private:
ChildWidget<ScrollArea> _scroll;
ChildWidget<InnerWidget> _inner;
ChildWidget<FixedBar> _fixedBar;
ChildWidget<PlainShadow> _fixedBarShadow1, _fixedBarShadow2;
ChildWidget<Ui::PlainShadow> _fixedBarShadow1, _fixedBarShadow2;
int _contentLeft = 0;

View file

@ -1249,7 +1249,7 @@ void StickerPanInner::selectInlineResult(int row, int column) {
} else if (document->loading()) {
document->cancel();
} else {
DocumentOpenClickHandler::doOpen(document, ActionOnLoadNone);
DocumentOpenClickHandler::doOpen(document, nullptr, ActionOnLoadNone);
}
} else if (auto inlineResult = item->getResult()) {
if (inlineResult->onChoose(item)) {

View file

@ -920,6 +920,11 @@ bool StickerData::setInstalled() const {
}
QString documentSaveFilename(const DocumentData *data, bool forceSavingAs = false, const QString already = QString(), const QDir &dir = QDir()) {
auto alreadySavingFilename = data->loadingFilePath();
if (!alreadySavingFilename.isEmpty()) {
return alreadySavingFilename;
}
QString name, filter, caption, prefix;
MimeType mimeType = mimeTypeForName(data->mime);
QStringList p = mimeType.globPatterns();
@ -953,19 +958,15 @@ QString documentSaveFilename(const DocumentData *data, bool forceSavingAs = fals
return saveFileName(caption, filter, prefix, name, forceSavingAs, dir);
}
void DocumentOpenClickHandler::doOpen(DocumentData *data, ActionOnLoad action) {
void DocumentOpenClickHandler::doOpen(DocumentData *data, HistoryItem *context, ActionOnLoad action) {
if (!data->date) return;
HistoryItem *item = App::hoveredLinkItem() ? App::hoveredLinkItem() : (App::contextItem() ? App::contextItem() : nullptr);
FullMsgId msgId;
if (item) {
msgId = item->fullId();
}
auto msgId = context ? context->fullId() : FullMsgId();
bool playVoice = data->voice() && audioPlayer();
bool playMusic = data->song() && audioPlayer();
bool playVideo = data->isVideo() && audioPlayer();
bool playAnimation = data->isAnimation() && item && item->getMedia();
const FileLocation &location(data->location(true));
bool playAnimation = data->isAnimation();
auto &location = data->location(true);
if (!location.isEmpty() || (!data->data().isEmpty() && (playVoice || playMusic || playVideo || playAnimation))) {
if (playVoice) {
AudioMsgId playing;
@ -992,9 +993,9 @@ void DocumentOpenClickHandler::doOpen(DocumentData *data, ActionOnLoad action) {
}
} else if (playVideo) {
if (!data->data().isEmpty()) {
App::wnd()->showDocument(data, item);
App::wnd()->showDocument(data, context);
} else if (location.accessEnable()) {
App::wnd()->showDocument(data, item);
App::wnd()->showDocument(data, context);
location.accessDisable();
} else {
auto filepath = location.name();
@ -1013,17 +1014,17 @@ void DocumentOpenClickHandler::doOpen(DocumentData *data, ActionOnLoad action) {
if (App::main()) App::main()->mediaMarkRead(data);
} else if (data->size < MediaViewImageSizeLimit) {
if (!data->data().isEmpty() && playAnimation) {
if (action == ActionOnLoadPlayInline && item->getMedia()) {
item->getMedia()->playInline(item);
if (action == ActionOnLoadPlayInline && context && context->getMedia()) {
context->getMedia()->playInline(context);
} else {
App::wnd()->showDocument(data, item);
App::wnd()->showDocument(data, context);
}
} else if (location.accessEnable()) {
if (data->isAnimation() || QImageReader(location.name()).canRead()) {
if (action == ActionOnLoadPlayInline && item && item->getMedia()) {
item->getMedia()->playInline(item);
if (action == ActionOnLoadPlayInline && context && context->getMedia()) {
context->getMedia()->playInline(context);
} else {
App::wnd()->showDocument(data, item);
App::wnd()->showDocument(data, context);
}
} else {
psOpenFile(location.name());
@ -1050,11 +1051,13 @@ void DocumentOpenClickHandler::doOpen(DocumentData *data, ActionOnLoad action) {
}
void DocumentOpenClickHandler::onClickImpl() const {
doOpen(document(), document()->voice() ? ActionOnLoadNone : ActionOnLoadOpen);
auto item = App::hoveredLinkItem() ? App::hoveredLinkItem() : (App::contextItem() ? App::contextItem() : nullptr);
doOpen(document(), item, document()->voice() ? ActionOnLoadNone : ActionOnLoadOpen);
}
void GifOpenClickHandler::onClickImpl() const {
doOpen(document(), ActionOnLoadPlayInline);
auto item = App::hoveredLinkItem() ? App::hoveredLinkItem() : (App::contextItem() ? App::contextItem() : nullptr);
doOpen(document(), item, ActionOnLoadPlayInline);
}
void DocumentSaveClickHandler::doSave(DocumentData *data, bool forceSavingAs) {
@ -1355,6 +1358,10 @@ bool DocumentData::loading() const {
return _loader && _loader != CancelledMtpFileLoader;
}
QString DocumentData::loadingFilePath() const {
return loading() ? _loader->fileName() : QString();
}
bool DocumentData::displayLoading() const {
return loading() ? (!_loader->loadingLocal() || !_loader->autoLoading()) : uploading();
}
@ -1378,11 +1385,8 @@ bool DocumentData::uploading() const {
}
void DocumentData::save(const QString &toFile, ActionOnLoad action, const FullMsgId &actionMsgId, LoadFromCloudSetting fromCloud, bool autoLoading) {
_actionOnLoad = action;
_actionOnLoadMsgId = actionMsgId;
if (loaded(FilePathResolveChecked)) {
const FileLocation &l(location(true));
auto &l = location(true);
if (!toFile.isEmpty()) {
if (!_data.isEmpty()) {
QFile f(toFile);
@ -1393,22 +1397,29 @@ void DocumentData::save(const QString &toFile, ActionOnLoad action, const FullMs
setLocation(FileLocation(StorageFilePartial, toFile));
Local::writeFileLocation(mediaKey(), FileLocation(mtpToStorageType(mtpc_storage_filePartial), toFile));
} else if (l.accessEnable()) {
QFile(l.name()).copy(toFile);
auto alreadyName = l.name();
if (alreadyName != toFile) {
QFile(alreadyName).copy(toFile);
}
l.accessDisable();
}
}
_actionOnLoad = action;
_actionOnLoadMsgId = actionMsgId;
performActionOnLoad();
return;
}
if (_loader == CancelledMtpFileLoader) _loader = 0;
if (_loader == CancelledMtpFileLoader) _loader = nullptr;
if (_loader) {
if (!_loader->setFileName(toFile)) {
cancel();
_loader = 0;
cancel(); // changes _actionOnLoad
_loader = nullptr;
}
}
_actionOnLoad = action;
_actionOnLoadMsgId = actionMsgId;
if (_loader) {
if (fromCloud == LoadFromCloudOrLocal) _loader->permitLoadFromCloud();
} else {

View file

@ -1083,6 +1083,7 @@ public:
};
bool loaded(FilePathResolveType type = FilePathResolveCached) const;
bool loading() const;
QString loadingFilePath() const;
bool displayLoading() const;
void save(const QString &toFile, ActionOnLoad action = ActionOnLoadNone, const FullMsgId &actionMsgId = FullMsgId(), LoadFromCloudSetting fromCloud = LoadFromCloudOrLocal, bool autoLoading = false);
void cancel();
@ -1320,7 +1321,7 @@ protected:
class DocumentOpenClickHandler : public DocumentClickHandler {
public:
using DocumentClickHandler::DocumentClickHandler;
static void doOpen(DocumentData *document, ActionOnLoad action = ActionOnLoadOpen);
static void doOpen(DocumentData *document, HistoryItem *context, ActionOnLoad action = ActionOnLoadOpen);
protected:
void onClickImpl() const override;
};

View file

@ -29,7 +29,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "boxes/aboutbox.h"
#include "media/media_audio.h"
#include "media/player/media_player_title_button.h"
#include "media/player/media_player_widget.h"
#include "media/player/media_player_panel.h"
#include "media/player/media_player_instance.h"
class TitleWidget::Hider : public TWidget {
@ -103,10 +103,16 @@ TitleWidget::TitleWidget(QWidget *parent) : TWidget(parent)
subscribe(Adaptive::Changed(), [this]() { updateAdaptiveLayout(); });
if (Media::Player::exists()) {
subscribe(Media::Player::instance()->createdNotifier(), [this](const Media::Player::CreatedEvent &e) {
subscribe(Media::Player::instance()->createdNotifier(), [this](const Media::Player::PanelEvent &e) {
if (!_player) {
_player.create(this);
_player->installEventFilter(e.widget);
updateControlsVisibility();
}
_player->installEventFilter(e.panel);
});
subscribe(Media::Player::instance()->destroyedNotifier(), [this](const Media::Player::PanelEvent &e) {
if (_player) {
_player.destroyDelayed();
updateControlsVisibility();
}
});

View file

@ -27,7 +27,7 @@ class MainWindow;
namespace Media {
namespace Player {
class TitleButton;
class CreatedEvent;
class PanelEvent;
} // namespace Player
} // namespace Media
class AudioMsgId;
@ -71,8 +71,6 @@ private:
void updateSystemButtonsVisibility();
void updateControlsPosition();
void handleMediaPlayerCreated(const Media::Player::CreatedEvent &e);
style::color statusColor;
class Hider;

View file

@ -84,9 +84,9 @@ void RectShadow::paint(Painter &p, const QRect &box, int shifty, Sides sides) {
bool wasSmooth = p.renderHints().testFlag(QPainter::SmoothPixmapTransform);
if (wasSmooth) p.setRenderHint(QPainter::SmoothPixmapTransform, false);
if (left) p.drawPixmap(box.left() - countsize + shifty, box.top() + (top ? minus : 0) + shifty, countsize - shifty, box.height() - (bottom ? minus : 0) - (top ? minus : 0), _left, 0, 0, count - rshifty, 1);
if (left) p.drawPixmap(box.left() - countsize + shifty, box.top() + (top ? (minus + shifty) : 0), countsize - shifty, box.height() - (bottom ? (minus - shifty) : 0) - (top ? (minus + shifty) : 0), _left, 0, 0, count - rshifty, 1);
if (top) p.drawPixmap(box.left() + (left ? minus : 0), box.top() - countsize + 2 * shifty, box.width() - (right ? minus : 0) - (left ? minus : 0), countsize - 2 * shifty, _top, 0, 0, 1, count - 2 * rshifty);
if (right) p.drawPixmap(box.left() + box.width(), box.top() + (top ? minus : 0) + shifty, countsize - shifty, box.height() - (bottom ? minus : 0) - (top ? minus : 0), _right, rshifty, 0, count - rshifty, 1);
if (right) p.drawPixmap(box.left() + box.width(), box.top() + (top ? (minus + shifty) : 0), countsize - shifty, box.height() - (bottom ? (minus - shifty) : 0) - (top ? (minus + shifty) : 0), _right, rshifty, 0, count - rshifty, 1);
if (bottom) p.drawPixmap(box.left() + (left ? minus : 0), box.top() + box.height(), box.width() - (right ? minus : 0) - (left ? minus : 0), countsize, _bottom, 0, 0, 1, count);
if (wasSmooth) p.setRenderHint(QPainter::SmoothPixmapTransform);
}

View file

@ -282,13 +282,13 @@ using QueryList = QList<Query>;
NeverFreedPointer<QueryList> Queries;
void StartCallback() {
Queries.makeIfNull();
Queries.createIfNull();
}
} // namespace
QueryId queryReadFile(const QString &caption, const QString &filter) {
Queries.makeIfNull();
Queries.createIfNull();
Queries->push_back(Query(Query::Type::ReadFile, caption, filter));
Global::RefHandleFileDialogQueue().call();
@ -296,7 +296,7 @@ QueryId queryReadFile(const QString &caption, const QString &filter) {
}
QueryId queryReadFiles(const QString &caption, const QString &filter) {
Queries.makeIfNull();
Queries.createIfNull();
Queries->push_back(Query(Query::Type::ReadFiles, caption, filter));
Global::RefHandleFileDialogQueue().call();
@ -304,7 +304,7 @@ QueryId queryReadFiles(const QString &caption, const QString &filter) {
}
QueryId queryWriteFile(const QString &caption, const QString &filter, const QString &filePath) {
Queries.makeIfNull();
Queries.createIfNull();
Queries->push_back(Query(Query::Type::WriteFile, caption, filter, filePath));
Global::RefHandleFileDialogQueue().call();
@ -312,7 +312,7 @@ QueryId queryWriteFile(const QString &caption, const QString &filter, const QStr
}
QueryId queryReadFolder(const QString &caption) {
Queries.makeIfNull();
Queries.createIfNull();
Queries->push_back(Query(Query::Type::ReadFolder, caption));
Global::RefHandleFileDialogQueue().call();

View file

@ -46,7 +46,7 @@ void stopModules() {
} // namespace
void registerModule(ModuleBase *module) {
styleModules.makeIfNull();
styleModules.createIfNull();
styleModules->push_back(module);
}

View file

@ -159,7 +159,7 @@ void MonoIcon::ensureLoaded() const {
if (_owningPixmap) {
_pixmap = createIconPixmap(_mask, _color);
} else {
iconPixmaps.makeIfNull();
iconPixmaps.createIfNull();
auto key = qMakePair(_mask, colorKey(_color->c));
auto i = iconPixmaps->constFind(key);
if (i == iconPixmaps->cend()) {

View file

@ -40,7 +40,7 @@ Manager::Manager(QWidget *parent) : QObject(parent) {
}
Manager *Manager::instance(QWidget *parent) {
_managers.makeIfNull();
_managers.createIfNull();
auto i = _managers->constFind(parent);
if (i == _managers->cend()) {
i = _managers->insert(parent, new Manager(parent));

View file

@ -78,42 +78,6 @@ QPixmap myGrab(TWidget *target, QRect rect) {
return result;
}
enum class Mode {
Shown,
ShownFast,
Hidden,
HiddenFast
};
void ToggleableShadow::setMode(Mode mode) {
if (mode == Mode::ShownFast || mode == Mode::HiddenFast) {
if (!_a_opacity.animating()) {
_a_opacity.finish();
update();
}
}
if (_shown && (mode == Mode::Hidden || mode == Mode::HiddenFast)) {
_shown = false;
if (mode == Mode::Hidden) {
_a_opacity.start([this] { update(); }, 1., 0., st::shadowToggleDuration);
}
} else if (!_shown && (mode == Mode::Shown || mode == Mode::ShownFast)) {
_shown = true;
if (mode == Mode::Shown) {
_a_opacity.start([this] { update(); }, 0., 1., st::shadowToggleDuration);
}
}
}
void ToggleableShadow::paintEvent(QPaintEvent *e) {
Painter p(this);
if (_a_opacity.animating(getms())) {
p.setOpacity(_a_opacity.current());
} else if (!_shown) {
return;
}
p.fillRect(e->rect(), _color);
}
void sendSynteticMouseEvent(QWidget *widget, QEvent::Type type, Qt::MouseButton button, const QPoint &globalPoint) {
if (auto windowHandle = widget->window()->windowHandle()) {
auto localPoint = windowHandle->mapFromGlobal(globalPoint);

View file

@ -120,52 +120,59 @@ public:
}
};
#define T_WIDGET public: \
TWidget *tparent() { \
return qobject_cast<TWidget*>(parentWidget()); \
} \
const TWidget *tparent() const { \
return qobject_cast<const TWidget*>(parentWidget()); \
} \
virtual void leaveToChildEvent(QEvent *e, QWidget *child) { /* e -- from enterEvent() of child TWidget */ \
} \
virtual void enterFromChildEvent(QEvent *e, QWidget *child) { /* e -- from leaveEvent() of child TWidget */ \
} \
void moveToLeft(int x, int y, int outerw = 0) { \
move(rtl() ? ((outerw > 0 ? outerw : parentWidget()->width()) - x - width()) : x, y); \
} \
void moveToRight(int x, int y, int outerw = 0) { \
move(rtl() ? x : ((outerw > 0 ? outerw : parentWidget()->width()) - x - width()), y); \
} \
QPoint myrtlpoint(int x, int y) const { \
return rtlpoint(x, y, width()); \
} \
QPoint myrtlpoint(const QPoint p) const { \
return rtlpoint(p, width()); \
} \
QRect myrtlrect(int x, int y, int w, int h) const { \
return rtlrect(x, y, w, h, width()); \
} \
QRect myrtlrect(const QRect &r) { \
return rtlrect(r, width()); \
} \
void rtlupdate(const QRect &r) { \
update(myrtlrect(r)); \
} \
void rtlupdate(int x, int y, int w, int h) { \
update(myrtlrect(x, y, w, h)); \
} \
#define T_WIDGET \
public: \
TWidget *tparent() { \
return qobject_cast<TWidget*>(parentWidget()); \
} \
const TWidget *tparent() const { \
return qobject_cast<const TWidget*>(parentWidget()); \
} \
virtual void leaveToChildEvent(QEvent *e, QWidget *child) { /* e -- from enterEvent() of child TWidget */ \
} \
virtual void enterFromChildEvent(QEvent *e, QWidget *child) { /* e -- from leaveEvent() of child TWidget */ \
} \
void moveToLeft(int x, int y, int outerw = 0) { \
move(rtl() ? ((outerw > 0 ? outerw : parentWidget()->width()) - x - width()) : x, y); \
} \
void moveToRight(int x, int y, int outerw = 0) { \
move(rtl() ? x : ((outerw > 0 ? outerw : parentWidget()->width()) - x - width()), y); \
} \
void setGeometryToLeft(int x, int y, int w, int h, int outerw = 0) { \
setGeometry(rtl() ? ((outerw > 0 ? outerw : parentWidget()->width()) - x - w) : x, y, w, h); \
} \
void setGeometryToRight(int x, int y, int w, int h, int outerw = 0) { \
setGeometry(rtl() ? x : ((outerw > 0 ? outerw : parentWidget()->width()) - x - w), y, w, h); \
} \
QPoint myrtlpoint(int x, int y) const { \
return rtlpoint(x, y, width()); \
} \
QPoint myrtlpoint(const QPoint p) const { \
return rtlpoint(p, width()); \
} \
QRect myrtlrect(int x, int y, int w, int h) const { \
return rtlrect(x, y, w, h, width()); \
} \
QRect myrtlrect(const QRect &r) const { \
return rtlrect(r, width()); \
} \
void rtlupdate(const QRect &r) { \
update(myrtlrect(r)); \
} \
void rtlupdate(int x, int y, int w, int h) { \
update(myrtlrect(x, y, w, h)); \
} \
protected: \
void enterEvent(QEvent *e) override { \
TWidget *p(tparent()); \
if (p) p->leaveToChildEvent(e, this); \
return enterEventHook(e); \
} \
void leaveEvent(QEvent *e) override { \
TWidget *p(tparent()); \
if (p) p->enterFromChildEvent(e, this); \
return leaveEventHook(e); \
}
void enterEvent(QEvent *e) override { \
TWidget *p(tparent()); \
if (p) p->leaveToChildEvent(e, this); \
return enterEventHook(e); \
} \
void leaveEvent(QEvent *e) override { \
TWidget *p(tparent()); \
if (p) p->enterFromChildEvent(e, this); \
return leaveEventHook(e); \
}
class TWidget : public QWidget {
Q_OBJECT
@ -239,48 +246,6 @@ protected:
void myEnsureResized(QWidget *target);
QPixmap myGrab(TWidget *target, QRect rect = QRect());
class PlainShadow : public TWidget {
public:
PlainShadow(QWidget *parent, const style::color &color) : TWidget(parent), _color(color) {
}
protected:
void paintEvent(QPaintEvent *e) override {
Painter(this).fillRect(e->rect(), _color->b);
}
private:
const style::color &_color;
};
class ToggleableShadow : public TWidget {
public:
ToggleableShadow(QWidget *parent, const style::color &color) : TWidget(parent), _color(color) {
}
enum class Mode {
Shown,
ShownFast,
Hidden,
HiddenFast
};
void setMode(Mode mode);
bool isFullyShown() const {
return _shown && !_a_opacity.animating();
}
protected:
void paintEvent(QPaintEvent *e) override;
private:
const style::color &_color;
FloatAnimation _a_opacity;
bool _shown = true;
};
class SingleDelayedCall : public QObject {
Q_OBJECT

View file

@ -0,0 +1,129 @@
/*
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-2016 John Preston, https://desktop.telegram.org
*/
#include "stdafx.h"
#include "ui/widgets/continuous_slider.h"
namespace Ui {
ContinuousSlider::ContinuousSlider(QWidget *parent) : TWidget(parent)
, _a_value(animation(this, &ContinuousSlider::step_value)) {
setCursor(style::cur_pointer);
}
float64 ContinuousSlider::value() const {
return a_value.current();
}
void ContinuousSlider::setDisabled(bool disabled) {
if (_disabled != disabled) {
_disabled = disabled;
setCursor(_disabled ? style::cur_default : style::cur_pointer);
update();
}
}
void ContinuousSlider::setValue(float64 value, bool animated) {
if (animated) {
a_value.start(value);
_a_value.start();
} else {
a_value = anim::fvalue(value, value);
_a_value.stop();
}
update();
}
void ContinuousSlider::setFadeOpacity(float64 opacity) {
_fadeOpacity = opacity;
update();
}
void ContinuousSlider::step_value(float64 ms, bool timer) {
float64 dt = ms / (2 * AudioVoiceMsgUpdateView);
if (dt >= 1) {
_a_value.stop();
a_value.finish();
} else {
a_value.update(qMin(dt, 1.), anim::linear);
}
if (timer) update();
}
void ContinuousSlider::mouseMoveEvent(QMouseEvent *e) {
if (_mouseDown) {
updateDownValueFromPos(e->pos());
}
}
float64 ContinuousSlider::computeValue(const QPoint &pos) const {
auto seekRect = myrtlrect(getSeekRect());
auto result = isHorizontal() ?
(pos.x() - seekRect.x()) / float64(seekRect.width()) :
(1. - (pos.y() - seekRect.y()) / float64(seekRect.height()));
return snap(result, 0., 1.);
}
void ContinuousSlider::mousePressEvent(QMouseEvent *e) {
_mouseDown = true;
_downValue = computeValue(e->pos());
update();
if (_changeProgressCallback) {
_changeProgressCallback(_downValue);
}
}
void ContinuousSlider::mouseReleaseEvent(QMouseEvent *e) {
if (_mouseDown) {
_mouseDown = false;
if (_changeFinishedCallback) {
_changeFinishedCallback(_downValue);
}
a_value = anim::fvalue(_downValue, _downValue);
_a_value.stop();
update();
}
}
void ContinuousSlider::updateDownValueFromPos(const QPoint &pos) {
_downValue = computeValue(pos);
update();
if (_changeProgressCallback) {
_changeProgressCallback(_downValue);
}
}
void ContinuousSlider::enterEvent(QEvent *e) {
setOver(true);
}
void ContinuousSlider::leaveEvent(QEvent *e) {
setOver(false);
}
void ContinuousSlider::setOver(bool over) {
if (_over == over) return;
_over = over;
auto from = _over ? 0. : 1., to = _over ? 1. : 0.;
_a_over.start([this] { update(); }, from, to, getOverDuration());
}
} // namespace Ui

View file

@ -0,0 +1,109 @@
/*
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-2016 John Preston, https://desktop.telegram.org
*/
#pragma once
namespace Ui {
class ContinuousSlider : public TWidget {
public:
ContinuousSlider(QWidget *parent);
enum class Direction {
Horizontal,
Vertical,
};
void setDirection(Direction direction) {
_direction = direction;
update();
}
float64 value() const;
void setValue(float64 value, bool animated);
void setFadeOpacity(float64 opacity);
void setDisabled(bool disabled);
using Callback = base::lambda_unique<void(float64)>;
void setChangeProgressCallback(Callback &&callback) {
_changeProgressCallback = std_::move(callback);
}
void setChangeFinishedCallback(Callback &&callback) {
_changeFinishedCallback = std_::move(callback);
}
bool isChanging() const {
return _mouseDown;
}
protected:
void mouseMoveEvent(QMouseEvent *e) override;
void mousePressEvent(QMouseEvent *e) override;
void mouseReleaseEvent(QMouseEvent *e) override;
void enterEvent(QEvent *e) override;
void leaveEvent(QEvent *e) override;
float64 fadeOpacity() const {
return _fadeOpacity;
}
float64 getCurrentValue(uint64 ms) {
_a_value.step(ms);
return _mouseDown ? _downValue : a_value.current();
}
float64 getCurrentOverFactor(uint64 ms) {
return _a_over.current(ms, _over ? 1. : 0.);
}
bool isDisabled() const {
return _disabled;
}
Direction getDirection() const {
return _direction;
}
bool isHorizontal() const {
return (_direction == Direction::Horizontal);
}
private:
virtual QRect getSeekRect() const = 0;
virtual float64 getOverDuration() const = 0;
void step_value(float64 ms, bool timer);
void setOver(bool over);
float64 computeValue(const QPoint &pos) const;
void updateDownValueFromPos(const QPoint &pos);
Direction _direction = Direction::Horizontal;
bool _disabled = false;
Callback _changeProgressCallback;
Callback _changeFinishedCallback;
bool _over = false;
FloatAnimation _a_over;
anim::fvalue a_value = { 0., 0. };
Animation _a_value;
bool _mouseDown = false;
float64 _downValue = 0.;
float64 _fadeOpacity = 1.;
};
} // namespace Ui

View file

@ -0,0 +1,73 @@
/*
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-2016 John Preston, https://desktop.telegram.org
*/
#include "stdafx.h"
#include "ui/widgets/filled_slider.h"
#include "styles/style_widgets.h"
namespace Ui {
FilledSlider::FilledSlider(QWidget *parent, const style::FilledSlider &st) : ContinuousSlider(parent)
, _st(st) {
}
QRect FilledSlider::getSeekRect() const {
return QRect(0, 0, width(), height());
}
float64 FilledSlider::getOverDuration() const {
return _st.duration;
}
void FilledSlider::paintEvent(QPaintEvent *e) {
Painter p(this);
p.setPen(Qt::NoPen);
p.setRenderHint(QPainter::HighQualityAntialiasing);
auto masterOpacity = fadeOpacity();
auto ms = getms();
auto disabled = isDisabled();
auto over = getCurrentOverFactor(ms);
auto lineWidth = _st.lineWidth + ((_st.fullWidth - _st.lineWidth) * over);
auto lineWidthRounded = qFloor(lineWidth);
auto lineWidthPartial = lineWidth - lineWidthRounded;
auto seekRect = getSeekRect();
auto value = getCurrentValue(ms);
auto from = seekRect.x(), mid = disabled ? from : qRound(from + value * seekRect.width()), end = from + seekRect.width();
if (mid > from) {
p.setOpacity(masterOpacity);
p.fillRect(from, height() - lineWidthRounded, (mid - from), lineWidthRounded, _st.activeFg);
if (lineWidthPartial > 0.01) {
p.setOpacity(masterOpacity * lineWidthPartial);
p.fillRect(from, height() - lineWidthRounded - 1, (mid - from), 1, _st.activeFg);
}
}
if (end > mid && over > 0) {
p.setOpacity(masterOpacity * over);
p.fillRect(mid, height() - lineWidthRounded, (end - mid), lineWidthRounded, _st.inactiveFg);
if (lineWidthPartial > 0.01) {
p.setOpacity(masterOpacity * over * lineWidthPartial);
p.fillRect(mid, height() - lineWidthRounded - 1, (end - mid), 1, _st.inactiveFg);
}
}
}
} // namespace Ui

View file

@ -0,0 +1,46 @@
/*
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-2016 John Preston, https://desktop.telegram.org
*/
#pragma once
#include "ui/widgets/continuous_slider.h"
namespace style {
struct FilledSlider;
} // namespace style
namespace Ui {
class FilledSlider : public ContinuousSlider {
public:
FilledSlider(QWidget *parent, const style::FilledSlider &st);
protected:
void paintEvent(QPaintEvent *e) override;
private:
QRect getSeekRect() const override;
float64 getOverDuration() const override;
const style::FilledSlider &_st;
};
} // namespace Ui

View file

@ -25,148 +25,78 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
namespace Ui {
MediaSlider::MediaSlider(QWidget *parent, const style::MediaSlider &st) : TWidget(parent)
, _st(st)
, _a_value(animation(this, &MediaSlider::step_value)) {
setCursor(style::cur_pointer);
MediaSlider::MediaSlider(QWidget *parent, const style::MediaSlider &st) : ContinuousSlider(parent)
, _st(st) {
}
float64 MediaSlider::value() const {
return a_value.current();
QRect MediaSlider::getSeekRect() const {
return isHorizontal()
? QRect(_st.seekSize.width() / 2, 0, width() - _st.seekSize.width(), height())
: QRect(0, _st.seekSize.height() / 2, width(), height() - _st.seekSize.width());
}
void MediaSlider::setDisabled(bool disabled) {
if (_disabled != disabled) {
_disabled = disabled;
setCursor(_disabled ? style::cur_default : style::cur_pointer);
update();
}
}
void MediaSlider::setValue(float64 value, bool animated) {
if (animated) {
a_value.start(value);
_a_value.start();
} else {
a_value = anim::fvalue(value, value);
_a_value.stop();
}
update();
}
void MediaSlider::setFadeOpacity(float64 opacity) {
_fadeOpacity = opacity;
update();
}
void MediaSlider::step_value(float64 ms, bool timer) {
float64 dt = ms / (2 * AudioVoiceMsgUpdateView);
if (dt >= 1) {
_a_value.stop();
a_value.finish();
} else {
a_value.update(qMin(dt, 1.), anim::linear);
}
if (timer) update();
}
int MediaSlider::lineLeft() const {
return (_st.seekSize.width() / 2);
}
int MediaSlider::lineWidth() const {
return (width() - _st.seekSize.width());
float64 MediaSlider::getOverDuration() const {
return _st.duration;
}
void MediaSlider::paintEvent(QPaintEvent *e) {
Painter p(this);
int radius = _st.width / 2;
p.setOpacity(_fadeOpacity);
p.setPen(Qt::NoPen);
p.setRenderHint(QPainter::HighQualityAntialiasing);
auto horizontal = isHorizontal();
auto ms = getms();
_a_value.step(ms);
auto over = _a_over.current(ms, _over ? 1. : 0.);
int skip = lineLeft();
int length = lineWidth();
float64 prg = _mouseDown ? _downValue : a_value.current();
int32 from = skip, mid = _disabled ? 0 : qRound(from + prg * length), end = from + length;
auto masterOpacity = fadeOpacity();
auto radius = _st.width / 2;
auto disabled = isDisabled();
auto over = getCurrentOverFactor(ms);
auto seekRect = getSeekRect();
auto value = getCurrentValue(ms);
// invert colors and value for vertical
if (!horizontal) value = 1. - value;
auto markerFrom = (horizontal ? seekRect.x() : seekRect.y());
auto markerLength = (horizontal ? seekRect.width() : seekRect.height());
auto from = _alwaysDisplayMarker ? 0 : markerFrom;
auto length = _alwaysDisplayMarker ? (horizontal ? width() : height()) : markerLength;
auto mid = disabled ? from : qRound(from + value * length);
auto end = from + length;
if (mid > from) {
p.setClipRect(0, 0, mid, height());
p.setOpacity(_fadeOpacity * (over * _st.activeOpacity + (1. - over) * _st.inactiveOpacity));
p.setBrush(_st.activeFg);
p.drawRoundedRect(from, (height() - _st.width) / 2, mid + radius - from, _st.width, radius, radius);
auto fromClipRect = horizontal ? QRect(0, 0, mid, height()) : QRect(0, 0, width(), mid);
auto fromRect = horizontal
? QRect(from, (height() - _st.width) / 2, mid + radius - from, _st.width)
: QRect((width() - _st.width) / 2, from, _st.width, mid + radius - from);
p.setClipRect(fromClipRect);
p.setOpacity(masterOpacity * (over * _st.activeOpacity + (1. - over) * _st.inactiveOpacity));
p.setBrush(horizontal ? _st.activeFg : _st.inactiveFg);
p.drawRoundedRect(fromRect, radius, radius);
}
if (end > mid) {
p.setClipRect(mid, 0, width() - mid, height());
p.setOpacity(_fadeOpacity);
p.setBrush(_st.inactiveFg);
p.drawRoundedRect(mid - radius, (height() - _st.width) / 2, end - (mid - radius), _st.width, radius, radius);
auto endClipRect = horizontal ? QRect(mid, 0, width() - mid, height()) : QRect(0, mid, width(), height() - mid);
auto endRect = horizontal
? QRect(mid - radius, (height() - _st.width) / 2, end - (mid - radius), _st.width)
: QRect((width() - _st.width) / 2, mid - radius, _st.width, end - (mid - radius));
p.setClipRect(endClipRect);
p.setOpacity(masterOpacity);
p.setBrush(horizontal ? _st.inactiveFg : _st.activeFg);
p.drawRoundedRect(endRect, radius, radius);
}
if (!_disabled && over > 0) {
int x = mid - skip;
p.setClipRect(rect());
p.setOpacity(_fadeOpacity * _st.activeOpacity);
auto seekButton = QRect(x, (height() - _st.seekSize.height()) / 2, _st.seekSize.width(), _st.seekSize.height());
int remove = ((1. - over) * _st.seekSize.width()) / 2.;
if (remove * 2 < _st.seekSize.width()) {
auto markerSizeRatio = disabled ? 0. : (_alwaysDisplayMarker ? 1. : over);
if (markerSizeRatio > 0) {
auto position = qRound(markerFrom + value * markerLength) - (horizontal ? seekRect.x() : seekRect.y());
auto seekButton = horizontal
? QRect(position, (height() - _st.seekSize.height()) / 2, _st.seekSize.width(), _st.seekSize.height())
: QRect((width() - _st.seekSize.width()) / 2, position, _st.seekSize.width(), _st.seekSize.height());
auto size = horizontal ? _st.seekSize.width() : _st.seekSize.height();
auto remove = static_cast<int>(((1. - markerSizeRatio) * size) / 2.);
if (remove * 2 < size) {
p.setClipRect(rect());
p.setOpacity(masterOpacity * _st.activeOpacity);
p.setBrush(_st.activeFg);
p.drawEllipse(seekButton.marginsRemoved(QMargins(remove, remove, remove, remove)));
}
}
}
void MediaSlider::mouseMoveEvent(QMouseEvent *e) {
if (_mouseDown) {
updateDownValueFromPos(e->pos().x());
}
}
void MediaSlider::mousePressEvent(QMouseEvent *e) {
_mouseDown = true;
_downValue = snap((e->pos().x() - lineLeft()) / float64(lineWidth()), 0., 1.);
update();
if (_changeProgressCallback) {
_changeProgressCallback(_downValue);
}
}
void MediaSlider::mouseReleaseEvent(QMouseEvent *e) {
if (_mouseDown) {
_mouseDown = false;
if (_changeFinishedCallback) {
_changeFinishedCallback(_downValue);
}
a_value = anim::fvalue(_downValue, _downValue);
_a_value.stop();
update();
}
}
void MediaSlider::updateDownValueFromPos(int pos) {
_downValue = snap((pos - lineLeft()) / float64(lineWidth()), 0., 1.);
update();
if (_changeProgressCallback) {
_changeProgressCallback(_downValue);
}
}
void MediaSlider::enterEvent(QEvent *e) {
setOver(true);
}
void MediaSlider::leaveEvent(QEvent *e) {
setOver(false);
}
void MediaSlider::setOver(bool over) {
if (_over == over) return;
_over = over;
auto from = _over ? 0. : 1., to = _over ? 1. : 0.;
_a_over.start([this] { update(); }, from, to, _st.duration);
}
} // namespace Ui

View file

@ -20,62 +20,32 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
*/
#pragma once
#include "ui/widgets/continuous_slider.h"
namespace style {
struct MediaSlider;
} // namespace style
namespace Ui {
class MediaSlider : public TWidget {
class MediaSlider : public ContinuousSlider {
public:
MediaSlider(QWidget *parent, const style::MediaSlider &st);
float64 value() const;
void setValue(float64 value, bool animated);
void setFadeOpacity(float64 opacity);
void setDisabled(bool disabled);
using Callback = base::lambda_unique<void(float64)>;
void setChangeProgressCallback(Callback &&callback) {
_changeProgressCallback = std_::move(callback);
}
void setChangeFinishedCallback(Callback &&callback) {
_changeFinishedCallback = std_::move(callback);
void setAlwaysDisplayMarker(bool alwaysDisplayMarker) {
_alwaysDisplayMarker = alwaysDisplayMarker;
update();
}
protected:
void paintEvent(QPaintEvent *e) override;
void mouseMoveEvent(QMouseEvent *e) override;
void mousePressEvent(QMouseEvent *e) override;
void mouseReleaseEvent(QMouseEvent *e) override;
void enterEvent(QEvent *e) override;
void leaveEvent(QEvent *e) override;
private:
void step_value(float64 ms, bool timer);
void setOver(bool over);
void updateDownValueFromPos(int pos);
int lineLeft() const;
int lineWidth() const;
QRect getSeekRect() const override;
float64 getOverDuration() const override;
const style::MediaSlider &_st;
bool _disabled = false;
Callback _changeProgressCallback;
Callback _changeFinishedCallback;
bool _over = false;
FloatAnimation _a_over;
anim::fvalue a_value = { 0., 0. };
Animation _a_value;
bool _mouseDown = false;
float64 _downValue = 0.;
float64 _fadeOpacity = 1.;
bool _alwaysDisplayMarker = false;
};

View file

@ -0,0 +1,56 @@
/*
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-2016 John Preston, https://desktop.telegram.org
*/
#include "stdafx.h"
#include "ui/widgets/shadow.h"
namespace Ui {
void ToggleableShadow::setMode(Mode mode) {
if (mode == Mode::ShownFast || mode == Mode::HiddenFast) {
if (!_a_opacity.animating()) {
_a_opacity.finish();
update();
}
}
if (_shown && (mode == Mode::Hidden || mode == Mode::HiddenFast)) {
_shown = false;
if (mode == Mode::Hidden) {
_a_opacity.start([this] { update(); }, 1., 0., st::shadowToggleDuration);
}
} else if (!_shown && (mode == Mode::Shown || mode == Mode::ShownFast)) {
_shown = true;
if (mode == Mode::Shown) {
_a_opacity.start([this] { update(); }, 0., 1., st::shadowToggleDuration);
}
}
}
void ToggleableShadow::paintEvent(QPaintEvent *e) {
Painter p(this);
if (_a_opacity.animating(getms())) {
p.setOpacity(_a_opacity.current());
} else if (!_shown) {
return;
}
p.fillRect(e->rect(), _color);
}
} // namespace Ui

View file

@ -0,0 +1,47 @@
#pragma once
namespace Ui {
class PlainShadow : public TWidget {
public:
PlainShadow(QWidget *parent, const style::color &color) : TWidget(parent), _color(color) {
}
protected:
void paintEvent(QPaintEvent *e) override {
Painter(this).fillRect(e->rect(), _color->b);
}
private:
const style::color &_color;
};
class ToggleableShadow : public TWidget {
public:
ToggleableShadow(QWidget *parent, const style::color &color) : TWidget(parent), _color(color) {
}
enum class Mode {
Shown,
ShownFast,
Hidden,
HiddenFast
};
void setMode(Mode mode);
bool isFullyShown() const {
return _shown && !_a_opacity.animating();
}
protected:
void paintEvent(QPaintEvent *e) override;
private:
const style::color &_color;
FloatAnimation _a_opacity;
bool _shown = true;
};
} // namespace Ui

View file

@ -27,19 +27,20 @@ namespace Ui {
template <typename Widget>
class WidgetSlideWrap : public TWidget {
public:
using UpdateCallback = base::lambda_unique<void()>;
WidgetSlideWrap(QWidget *parent, Widget *entity
, style::margins entityPadding
, base::lambda_unique<void()> &&updateCallback
, UpdateCallback &&updateCallback
, int duration = st::widgetSlideDuration) : TWidget(parent)
, _entity(entity)
, _padding(entityPadding)
, _duration(duration)
, _updateCallback(std_::move(updateCallback))
, _a_height(animation(this, &WidgetSlideWrap<Widget>::step_height)) {
entity->setParent(this);
entity->moveToLeft(_padding.left(), _padding.top());
_realSize = entity->rect().marginsAdded(_padding).size();
entity->installEventFilter(this);
, _entity(entity)
, _padding(entityPadding)
, _duration(duration)
, _updateCallback(std_::move(updateCallback))
, _a_height(animation(this, &WidgetSlideWrap<Widget>::step_height)) {
_entity->setParent(this);
_entity->moveToLeft(_padding.left(), _padding.top());
_realSize = _entity->rect().marginsAdded(_padding).size();
_entity->installEventFilter(this);
resize(_realSize);
}
@ -91,6 +92,7 @@ public:
}
void showFast() {
show();
_a_height.stop();
resize(_realSize);
if (_updateCallback) {
@ -152,7 +154,7 @@ private:
bool _inResizeToWidth = false;
style::margins _padding;
int _duration;
base::lambda_unique<void()> _updateCallback;
UpdateCallback _updateCallback;
style::size _realSize;
int _forceHeight = -1;

View file

@ -43,6 +43,14 @@ MediaSlider {
duration: int;
}
FilledSlider {
fullWidth: pixels;
lineWidth: pixels;
activeFg: color;
inactiveFg: color;
duration: int;
}
widgetSlideDuration: 200;
discreteSliderHeight: 39px;

View file

@ -83,7 +83,7 @@ void ChatBackground::setTile(bool tile) {
}
ChatBackground *chatBackground() {
instance.makeIfNull();
instance.createIfNull();
return instance.data();
}

View file

@ -57,7 +57,7 @@ internal::Widget::Direction notificationShiftDirection() {
} // namespace
void start() {
ManagerInstance.makeIfNull();
ManagerInstance.createIfNull();
}
Manager *manager() {

View file

@ -0,0 +1,44 @@
/*
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-2016 John Preston, https://desktop.telegram.org
*/
#include "stdafx.h"
#include "window/player_wrap_widget.h"
#include "ui/widgets/shadow.h"
namespace Window {
PlayerWrapWidget::PlayerWrapWidget(QWidget *parent, UpdateCallback &&updateCallback) : Parent(parent
, new Media::Player::Widget(parent)
, style::margins(0, 0, 0, 0)
, std_::move(updateCallback)) {
}
void PlayerWrapWidget::resizeEvent(QResizeEvent *e) {
updateShadowGeometry();
Parent::resizeEvent(e);
}
void PlayerWrapWidget::updateShadowGeometry() {
auto skip = Adaptive::OneColumn() ? 0 : st::lineWidth;
entity()->setShadowGeometryToLeft(skip, height() - st::lineWidth, width() - skip, st::lineWidth);
}
} // namespace Window

View file

@ -0,0 +1,40 @@
#pragma once
#include "ui/widgets/widget_slide_wrap.h"
#include "media/player/media_player_widget.h"
namespace Ui {
class PlainShadow;
} // namespace Ui
namespace Window {
class PlayerWrapWidget : public Ui::WidgetSlideWrap<Media::Player::Widget> {
using Parent = Ui::WidgetSlideWrap<Media::Player::Widget>;
public:
using UpdateCallback = Parent::UpdateCallback;
PlayerWrapWidget(QWidget *parent, UpdateCallback &&updateCallback);
void updateAdaptiveLayout() {
updateShadowGeometry();
}
void showShadow() {
entity()->showShadow();
}
void hideShadow() {
entity()->hideShadow();
}
int contentHeight() const {
return height() - st::lineWidth;
}
protected:
void resizeEvent(QResizeEvent *e) override;
private:
void updateShadowGeometry();
};
} // namespace Window

View file

@ -129,8 +129,6 @@
'<(src_loc)/overviewwidget.h',
'<(src_loc)/passcodewidget.cpp',
'<(src_loc)/passcodewidget.h',
'<(src_loc)/playerwidget.cpp',
'<(src_loc)/playerwidget.h',
'<(src_loc)/localimageloader.cpp',
'<(src_loc)/localimageloader.h',
'<(src_loc)/localstorage.cpp',
@ -268,6 +266,8 @@
'<(src_loc)/media/player/media_player_instance.h',
'<(src_loc)/media/player/media_player_list.cpp',
'<(src_loc)/media/player/media_player_list.h',
'<(src_loc)/media/player/media_player_panel.cpp',
'<(src_loc)/media/player/media_player_panel.h',
'<(src_loc)/media/player/media_player_title_button.cpp',
'<(src_loc)/media/player/media_player_title_button.h',
'<(src_loc)/media/player/media_player_volume_controller.cpp',
@ -467,13 +467,19 @@
'<(src_loc)/ui/toast/toast_manager.h',
'<(src_loc)/ui/toast/toast_widget.cpp',
'<(src_loc)/ui/toast/toast_widget.h',
'<(src_loc)/ui/widgets/continuous_slider.cpp',
'<(src_loc)/ui/widgets/continuous_slider.h',
'<(src_loc)/ui/widgets/discrete_slider.cpp',
'<(src_loc)/ui/widgets/discrete_slider.h',
'<(src_loc)/ui/widgets/filled_slider.cpp',
'<(src_loc)/ui/widgets/filled_slider.h',
'<(src_loc)/ui/widgets/label_simple.cpp',
'<(src_loc)/ui/widgets/label_simple.h',
'<(src_loc)/ui/widgets/media_slider.cpp',
'<(src_loc)/ui/widgets/media_slider.h',
'<(src_loc)/ui/widgets/shadow.cpp',
'<(src_loc)/ui/widgets/shadow.h',
'<(src_loc)/ui/widgets/widget_slide_wrap.h',
'<(src_loc)/ui/widgets/discrete_slider.cpp',
'<(src_loc)/ui/widgets/discrete_slider.h',
'<(src_loc)/ui/animation.cpp',
'<(src_loc)/ui/animation.h',
'<(src_loc)/ui/button.cpp',
@ -514,6 +520,8 @@
'<(src_loc)/window/notifications_manager_default.h',
'<(src_loc)/window/notifications_utilities.cpp',
'<(src_loc)/window/notifications_utilities.h',
'<(src_loc)/window/player_wrap_widget.cpp',
'<(src_loc)/window/player_wrap_widget.h',
'<(src_loc)/window/section_widget.cpp',
'<(src_loc)/window/section_widget.h',
'<(src_loc)/window/slide_animation.cpp',