New audio player done (without playlist).

This commit is contained in:
John Preston 2016-09-23 19:04:26 +03:00
parent a8f3582cb1
commit f970ac3163
57 changed files with 1358 additions and 495 deletions

View File

@ -1,33 +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
*/
// Legacy styles
using "basic_types.style";
using "basic.style";
using "boxes/boxes.style";
using "dialogs/dialogs.style";
using "history/history.style";
using "overview/overview.style";
using "profile/profile.style";
using "settings/settings.style";
using "media/view/mediaview.style";
using "ui/widgets/widgets.style";

View File

@ -2136,8 +2136,6 @@ namespace {
}
void initMedia() {
audioInit();
if (!::monofont) {
QString family;
tryFontFamily(family, qsl("Consolas"));
@ -2210,21 +2208,19 @@ namespace {
}
void deinitMedia() {
audioFinish();
delete ::emoji;
::emoji = 0;
delete ::emojiLarge;
::emojiLarge = 0;
for (int32 j = 0; j < 4; ++j) {
for (int32 i = 0; i < RoundCornersCount; ++i) {
for (int j = 0; j < 4; ++j) {
for (int i = 0; i < RoundCornersCount; ++i) {
delete ::corners[i].p[j]; ::corners[i].p[j] = nullptr;
}
delete ::cornersMaskSmall[j]; ::cornersMaskSmall[j] = nullptr;
delete ::cornersMaskLarge[j]; ::cornersMaskLarge[j] = nullptr;
}
for (CornersMap::const_iterator i = ::cornersMap.cbegin(), e = ::cornersMap.cend(); i != e; ++i) {
for (int32 j = 0; j < 4; ++j) {
for (auto i = ::cornersMap.cbegin(), e = ::cornersMap.cend(); i != e; ++i) {
for (int j = 0; j < 4; ++j) {
delete i->p[j];
}
}

View File

@ -35,6 +35,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "observer_peer.h"
#include "core/observer.h"
#include "window/chat_background.h"
#include "media/player/media_player_instance.h"
namespace {
void mtpStateChanged(int32 dc, int32 state) {
@ -722,6 +723,7 @@ AppClass::AppClass() : QObject()
style::startManager();
anim::startManager();
historyInit();
Media::Player::start();
DEBUG_LOG(("Application Info: inited..."));
@ -1108,6 +1110,7 @@ AppClass::~AppClass() {
Window::chatBackground()->reset();
Media::Player::finish();
style::stopManager();
Local::finish();

View File

@ -27,8 +27,10 @@ namespace codegen {
namespace common {
namespace {
QString WorkingPath = ".";
std::string relativeLocalPath(const QString &filepath) {
auto name = QFile::encodeName(QDir().relativeFilePath(filepath));
auto name = QFile::encodeName(QDir(WorkingPath).relativeFilePath(filepath));
return name.constData();
}
@ -43,5 +45,9 @@ LogStream logError(int code, const QString &filepath, int line) {
return LogStream(std::cerr);
}
void logSetWorkingPath(const QString &workingpath) {
WorkingPath = workingpath;
}
} // namespace common
} // namespace codegen

View File

@ -72,6 +72,8 @@ LogStream operator<<(LogStream &&stream, T &&value) {
// logError(kErrorFileTooLarge, filepath) << "file too large, size=" << size;
LogStream logError(int code, const QString &filepath, int line = 0);
void logSetWorkingPath(const QString &workingpath);
static constexpr int kErrorInternal = 666;
} // namespace common

View File

@ -22,6 +22,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include <ostream>
#include <QtCore/QCoreApplication>
#include <QtCore/QDir>
#include "codegen/common/logging.h"
namespace codegen {
@ -32,6 +33,7 @@ constexpr int kErrorIncludePathExpected = 901;
constexpr int kErrorOutputPathExpected = 902;
constexpr int kErrorInputPathExpected = 903;
constexpr int kErrorSingleInputPathExpected = 904;
constexpr int kErrorWorkingPathExpected = 905;
} // namespace
@ -77,6 +79,17 @@ Options parseOptions() {
} else if (arg.startsWith("-o")) {
result.outputPath = arg.mid(2);
// Working path
} else if (arg == "-w") {
if (++i == count) {
logError(kErrorWorkingPathExpected, "Command Line") << "working path expected after -w";
return Options();
} else {
common::logSetWorkingPath(args.at(i));
}
} else if (arg.startsWith("-w")) {
common::logSetWorkingPath(arg.mid(2));
// Input path
} else {
if (result.inputPath.isEmpty()) {

View File

@ -340,7 +340,7 @@ inline constexpr typename remove_reference<T>::type &&move(T &&value) noexcept {
}
template <typename T>
void swap(T &a, T &b) {
void swap_moveable(T &a, T &b) {
T tmp = move(a);
a = move(b);
b = move(tmp);
@ -517,7 +517,7 @@ struct is_base_of {
template <typename T>
T createAndSwap(T &value) {
T result = T();
std_::swap(result, value);
std_::swap_moveable(result, value);
return std_::move(result);
}

View File

@ -335,7 +335,7 @@ class CommonObservable {
public:
using Handler = typename CommonObservableData<EventType>::Handler;
Subscription subscribe(Handler &&handler) {
Subscription add_subscription(Handler &&handler) {
if (!_data) {
_data = MakeShared<ObservableData<EventType>>(this);
}
@ -360,6 +360,9 @@ public:
this->_data->notify(std_::move(event), sync);
}
}
void notify(const EventType &event, bool sync = false) {
notify(EventType(event));
}
};
@ -562,7 +565,7 @@ class Subscriber {
protected:
template <typename EventType, typename Lambda>
int subscribe(base::Observable<EventType> &observable, Lambda &&handler) {
_subscriptions.push_back(observable.subscribe(std_::forward<Lambda>(handler)));
_subscriptions.push_back(observable.add_subscription(std_::forward<Lambda>(handler)));
return _subscriptions.size() - 1;
}

View File

@ -39,9 +39,9 @@ public:
, _plaindata(createAndSwap(other._plaindata)) {
}
vector_of_moveable &operator=(vector_of_moveable &&other) {
std_::swap(_size, other._size);
std_::swap(_capacity, other._capacity);
std_::swap(_plaindata, other._plaindata);
std_::swap_moveable(_size, other._size);
std_::swap_moveable(_capacity, other._capacity);
std_::swap_moveable(_plaindata, other._plaindata);
return *this;
}
@ -165,7 +165,7 @@ private:
new (newLocation) T(std_::move(*oldLocation));
oldLocation->~T();
}
std_::swap(_plaindata, newPlainData);
std_::swap_moveable(_plaindata, newPlainData);
_capacity = newCapacity;
operator delete[](newPlainData);
}

View File

@ -584,8 +584,8 @@ struct Data {
int32 DebugLoggingFlags = 0;
float64 SongVolume = 0.9;
float64 VideoVolume = 0.9;
float64 SongVolume = kDefaultVolume;
float64 VideoVolume = kDefaultVolume;
// config
int32 ChatSizeMax = 200;

View File

@ -31,7 +31,6 @@ class ItemBase;
} // namespace Layout
} // namespace InlineBots
namespace App {
void sendBotCommand(PeerData *peer, UserData *bot, const QString &cmd, MsgId replyTo = 0);
@ -236,6 +235,8 @@ bool started();
void start();
void finish();
constexpr float64 kDefaultVolume = 0.9;
DeclareReadOnlyVar(uint64, LaunchId);
DeclareRefVar(SingleDelayedCall, HandleHistoryUpdate);
DeclareRefVar(SingleDelayedCall, HandleUnreadCounterUpdate);

View File

@ -1177,7 +1177,7 @@ HistoryBlock *History::prepareBlockForAddingItem() {
return _buildingFrontBlock->block;
}
HistoryBlock *result = _buildingFrontBlock->block = new HistoryBlock(this);
auto result = _buildingFrontBlock->block = new HistoryBlock(this);
if (_buildingFrontBlock->expectedItemsCount > 0) {
result->items.reserve(_buildingFrontBlock->expectedItemsCount + 1);
}
@ -1194,7 +1194,7 @@ HistoryBlock *History::prepareBlockForAddingItem() {
return blocks.back();
}
HistoryBlock *result = new HistoryBlock(this);
auto result = new HistoryBlock(this);
result->setIndexInHistory(blocks.size());
blocks.push_back(result);
@ -1206,7 +1206,7 @@ void History::addItemToBlock(HistoryItem *item) {
t_assert(item != nullptr);
t_assert(item->detached());
HistoryBlock *block = prepareBlockForAddingItem();
auto block = prepareBlockForAddingItem();
item->attachToBlock(block, block->items.size());
block->items.push_back(item);
@ -1231,21 +1231,21 @@ void History::addOlderSlice(const QVector<MTPMessage> &slice) {
for (auto i = slice.cend(), e = slice.cbegin(); i != e;) {
--i;
HistoryItem *adding = createItem(*i, false, true);
auto adding = createItem(*i, false, true);
if (!adding) continue;
addItemToBlock(adding);
}
HistoryBlock *block = finishBuildingFrontBlock();
auto block = finishBuildingFrontBlock();
if (!block) {
// If no items were added it means we've loaded everything old.
oldLoaded = true;
} else if (loadedAtBottom()) { // add photos to overview and authors to lastAuthors / lastParticipants
bool channel = isChannel();
int32 mask = 0;
QList<UserData*> *lastAuthors = 0;
OrderedSet<PeerData*> *markupSenders = 0;
QList<UserData*> *lastAuthors = nullptr;
OrderedSet<PeerData*> *markupSenders = nullptr;
if (peer->isChat()) {
lastAuthors = &peer->asChat()->lastAuthors;
markupSenders = &peer->asChat()->markupSenders;
@ -1254,7 +1254,7 @@ void History::addOlderSlice(const QVector<MTPMessage> &slice) {
markupSenders = &peer->asChannel()->mgInfo->markupSenders;
}
for (int32 i = block->items.size(); i > 0; --i) {
HistoryItem *item = block->items[i - 1];
auto item = block->items[i - 1];
mask |= item->addToOverview(AddToOverviewFront);
if (item->from()->id) {
if (lastAuthors) { // chats
@ -1272,7 +1272,7 @@ void History::addOlderSlice(const QVector<MTPMessage> &slice) {
if (item->author()->id) {
if (markupSenders) { // chats with bots
if (!lastKeyboardInited && item->definesReplyKeyboard() && !item->out()) {
MTPDreplyKeyboardMarkup::Flags markupFlags = item->replyKeyboardFlags();
auto markupFlags = item->replyKeyboardFlags();
if (!(markupFlags & MTPDreplyKeyboardMarkup::Flag::f_selective) || item->mentionsMe()) {
bool wasKeyboardHide = markupSenders->contains(item->author());
if (!wasKeyboardHide) {
@ -1340,7 +1340,7 @@ void History::addNewerSlice(const QVector<MTPMessage> &slice) {
bool atLeastOneAdded = false;
for (auto i = slice.cend(), e = slice.cbegin(); i != e;) {
--i;
HistoryItem *adding = createItem(*i, false, true);
auto adding = createItem(*i, false, true);
if (!adding) continue;
addItemToBlock(adding);

View File

@ -8723,7 +8723,7 @@ void HistoryWidget::paintEvent(QPaintEvent *e) {
if (r != rect()) {
p.setClipRect(r);
}
bool hasTopBar = !App::main()->topBar()->isHidden(), hasPlayer = !App::main()->player()->isHidden();
bool hasTopBar = !App::main()->topBar()->isHidden();
if (_a_show.animating()) {
int retina = cIntRetinaFactor();
@ -8741,7 +8741,7 @@ void HistoryWidget::paintEvent(QPaintEvent *e) {
}
QRect fill(0, 0, width(), App::main()->height());
int fromy = (hasTopBar ? (-st::topBarHeight) : 0) + (hasPlayer ? (-st::playerHeight) : 0), x = 0, y = 0;
int fromy = (hasTopBar ? (-st::topBarHeight) : 0), x = 0, y = 0;
QPixmap cached = App::main()->cachedBackground(fill, x, y);
if (cached.isNull()) {
auto &pix = Window::chatBackground()->image();

View File

@ -24,6 +24,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "styles/style_overview.h"
#include "inline_bots/inline_bot_result.h"
#include "media/media_clip_reader.h"
#include "media/player/media_player_instance.h"
#include "localstorage.h"
#include "mainwidget.h"
#include "lang.h"
@ -874,7 +875,7 @@ bool File::updateStatusText() const {
realDuration = playbackState.duration / (playbackState.frequency ? playbackState.frequency : AudioVoiceMsgFrequency);
showPause = (playbackState.state == AudioPlayerPlaying || playbackState.state == AudioPlayerResuming || playbackState.state == AudioPlayerStarting);
}
if (!showPause && (playing == AudioMsgId(document, FullMsgId())) && App::main() && App::main()->player()->seekingSong(playing)) {
if (!showPause && (playing == AudioMsgId(document, FullMsgId())) && Media::Player::exists() && Media::Player::instance()->isSeeking()) {
showPause = true;
}
}

View File

@ -50,8 +50,8 @@ 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_button.h"
#include "media/player/media_player_widget.h"
#include "media/player/media_player_instance.h"
#include "core/qthelp_regex.h"
#include "core/qthelp_url.h"
#include "window/chat_background.h"
@ -98,9 +98,14 @@ MainWidget::MainWidget(MainWindow *window) : TWidget(window)
connect(_topBar, SIGNAL(clicked()), this, SLOT(onTopBarClick()));
connect(_history, SIGNAL(historyShown(History*,MsgId)), this, SLOT(onHistoryShown(History*,MsgId)));
connect(&updateNotifySettingTimer, SIGNAL(timeout()), this, SLOT(onUpdateNotifySettings()));
if (audioPlayer()) {
connect(audioPlayer(), SIGNAL(updated(const AudioMsgId&)), this, SLOT(audioPlayProgress(const AudioMsgId&)));
if (auto player = audioPlayer()) {
subscribe(player, [this](const AudioMsgId &audioId) {
if (audioId.type() != AudioMsgId::Type::Video) {
handleAudioUpdate(audioId);
}
});
}
connect(&_updateMutedTimer, SIGNAL(timeout()), this, SLOT(onUpdateMuted()));
connect(&_viewsIncrementTimer, SIGNAL(timeout()), this, SLOT(onViewsIncrement()));
@ -1542,11 +1547,7 @@ void MainWidget::ui_autoplayMediaInlineAsync(qint32 channelId, qint32 msgId) {
}
}
void MainWidget::audioPlayProgress(const AudioMsgId &audioId) {
if (audioId.type() == AudioMsgId::Type::Video) {
return;
}
void MainWidget::handleAudioUpdate(const AudioMsgId &audioId) {
AudioMsgId playing;
auto playbackState = audioPlayer()->currentState(&playing, audioId.type());
if (playing == audioId && playbackState.state == AudioPlayerStoppedAtStart) {
@ -1563,6 +1564,13 @@ void MainWidget::audioPlayProgress(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) {
@ -1600,28 +1608,35 @@ void MainWidget::closePlayer() {
}
void MainWidget::documentLoadProgress(FileLoader *loader) {
mtpFileLoader *l = loader ? loader->mtpLoader() : 0;
if (!l) return;
if (auto mtpLoader = loader ? loader->mtpLoader() : nullptr) {
documentLoadProgress(App::document(mtpLoader->objId()));
}
}
DocumentData *document = App::document(l->objId());
void MainWidget::documentLoadProgress(DocumentData *document) {
if (document->loaded()) {
document->performActionOnLoad();
}
const DocumentItems &items(App::documentItems());
DocumentItems::const_iterator i = items.constFind(document);
auto &items = App::documentItems();
auto i = items.constFind(document);
if (i != items.cend()) {
for (HistoryItemsMap::const_iterator j = i->cbegin(), e = i->cend(); j != e; ++j) {
for (auto j = i->cbegin(), e = i->cend(); j != e; ++j) {
Ui::repaintHistoryItem(j.key());
}
}
App::wnd()->documentUpdated(document);
if (!document->loaded() && document->loading() && document->song() && audioPlayer()) {
AudioMsgId playing;
auto playbackState = audioPlayer()->currentState(&playing, AudioMsgId::Type::Song);
if (playing.audio() == document && !_player->isHidden()) {
_player->updateState(playing, playbackState);
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);
}
}
}
@ -2664,9 +2679,7 @@ inline int chatsListWidth(int windowWidth) {
void MainWidget::resizeEvent(QResizeEvent *e) {
int32 tbh = _topBar->isHidden() ? 0 : st::topBarHeight;
if (_mediaPlayer) {
_mediaPlayer->moveToRight(0, 0);
}
updateMediaPlayerPosition();
if (Adaptive::OneColumn()) {
_dialogsWidth = width();
_player->setGeometry(0, 0, _dialogsWidth, _player->height());
@ -2700,6 +2713,12 @@ void MainWidget::resizeEvent(QResizeEvent *e) {
_contentScrollAddToY = 0;
}
void MainWidget::updateMediaPlayerPosition() {
if (_mediaPlayer) {
_mediaPlayer->moveToRight(0, 0);
}
}
int MainWidget::contentScrollAddToY() const {
return _contentScrollAddToY;
}

View File

@ -139,10 +139,6 @@ class MainWidget : public TWidget, public RPCSender, private base::Subscriber {
public:
MainWidget(MainWindow *window);
void paintEvent(QPaintEvent *e) override;
void resizeEvent(QResizeEvent *e) override;
void keyPressEvent(QKeyEvent *e) override;
bool needBackButton();
// Temporary methods, while top bar was not done inside HistoryWidget / OverviewWidget.
@ -382,6 +378,8 @@ public:
void closePlayer();
void documentLoadProgress(DocumentData *document);
void app_sendBotCallback(const HistoryMessageReplyMarkup::Button *button, const HistoryItem *msg, int row, int col);
void ui_repaintHistoryItem(const HistoryItem *item);
@ -423,7 +421,6 @@ signals:
public slots:
void webPagesUpdate();
void audioPlayProgress(const AudioMsgId &audioId);
void documentLoadProgress(FileLoader *loader);
void documentLoadFailed(FileLoader *loader, bool started);
void documentLoadRetry();
@ -479,8 +476,15 @@ public slots:
void onDeletePhotoSure();
protected:
void paintEvent(QPaintEvent *e) override;
void resizeEvent(QResizeEvent *e) override;
void keyPressEvent(QKeyEvent *e) override;
private:
void updateAdaptiveLayout();
void handleAudioUpdate(const AudioMsgId &audioId);
void updateMediaPlayerPosition();
void sendReadRequest(PeerData *peer, MsgId upTo);
void channelReadDone(PeerData *peer, const MTPBool &result);

View File

@ -349,7 +349,7 @@ void AudioPlayer::onUpdated(const AudioMsgId &audio) {
if (audio.type() == AudioMsgId::Type::Video) {
videoSoundProgress(audio);
}
notify(AudioMsgId(audio));
notify(audio);
}
void AudioPlayer::onError(const AudioMsgId &audio) {

View File

@ -20,6 +20,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
*/
using "basic.style";
using "ui/widgets/widgets.style";
mediaPlayerTitleButtonSize: size(titleHeight, titleHeight);
mediaPlayerTitleButtonInner: size(25px, 25px);
@ -37,6 +38,9 @@ mediaPlayerMarginBottom: 10px;
mediaPlayerWidth: 344px;
mediaPlayerCoverHeight: 102px;
mediaPlayerActiveFg: #54b5ed;
mediaPlayerInactiveFg: #dfebf2;
mediaPlayerPlayButton: IconButton {
width: 32px;
height: 32px;
@ -45,7 +49,7 @@ mediaPlayerPlayButton: IconButton {
overOpacity: 1.;
icon: icon {
{ "player_play", #54b5ed, point(6px, 7px) },
{ "player_play", mediaPlayerActiveFg, point(6px, 7px) },
};
iconPosition: point(0px, 0px);
downIconPosition: point(0px, 0px);
@ -53,41 +57,88 @@ mediaPlayerPlayButton: IconButton {
duration: 0;
}
mediaPlayerPauseIcon: icon {
{ "player_pause", #54b5ed, point(9px, 8px) }
{ "player_pause", mediaPlayerActiveFg, point(9px, 8px) }
};
mediaPlayerRepeatButton: IconButton(mediaPlayerPlayButton) {
width: 31px;
icon: icon {
{ "player_repeat", #54b5ed, point(9px, 9px)}
{ "player_repeat", mediaPlayerActiveFg, point(9px, 9px)}
};
}
mediaPlayerRepeatDisabledIcon: icon {
{ "player_repeat", mediaPlayerInactiveFg, point(9px, 9px)}
};
mediaPlayerPreviousButton: IconButton(mediaPlayerPlayButton) {
width: 37px;
icon: icon {
{ "player_previous", mediaPlayerActiveFg, point(10px, 10px) },
};
}
mediaPlayerPreviousDisabledIcon: icon {
{ "player_previous", mediaPlayerInactiveFg, point(10px, 10px) },
};
mediaPlayerNextButton: IconButton(mediaPlayerPreviousButton) {
icon: icon {
{ "player_next", mediaPlayerActiveFg, point(10px, 10px) },
};
}
mediaPlayerNextDisabledIcon: icon {
{ "player_next", mediaPlayerInactiveFg, point(10px, 10px) },
};
mediaPlayerPadding: 18px;
mediaPlayerNameTop: 24px;
mediaPlayerPlayLeft: 9px;
mediaPlayerPlaySkip: 7px;
mediaPlayerPlayTop: 58px;
mediaPlayerNameFont: normalFont;
mediaPlayerNameFg: windowTextFg;
mediaPlayerTimeFont: normalFont;
mediaPlayerTimeFg: windowSubTextFg;
mediaPlayerPlaybackTop: 32px;
mediaPlayerPlaybackPadding: 8px;
mediaPlayerPlaybackBg: #54b5ed;
mediaPlayerPlaybackLine: 3px;
mediaPlayerPlayback: MediaSlider {
width: 3px;
activeFg: mediaPlayerActiveFg;
inactiveFg: mediaPlayerInactiveFg;
activeOpacity: 1.;
inactiveOpacity: 1.;
seekSize: size(9px, 9px);
duration: 150;
}
mediaPlayerVolumeRight: 50px;
mediaPlayerName: flatLabel(labelDefFlat) {
maxHeight: 20px;
textFg: windowTextFg;
}
mediaPlayerTime: LabelSimple(defaultLabelSimple) {
textFg: windowSubTextFg;
}
mediaPlayerVolumeTop: 65px;
mediaPlayerVolumeRight: 51px;
mediaPlayerVolumeWidth: 86px;
mediaPlayerVolumeLength: 64px;
mediaPlayerVolumeIcon0: icon {
{ "player_volume0", #54b5ed },
{ "player_volume0", mediaPlayerActiveFg },
};
mediaPlayerVolumeIcon1: icon {
{ "player_volume1", #54b5ed },
{ "player_volume1", mediaPlayerActiveFg },
};
mediaPlayerVolumeIcon2: icon {
{ "player_volume2", #54b5ed },
{ "player_volume2", mediaPlayerActiveFg },
};
mediaPlayerVolumeIcon3: icon {
{ "player_volume3", #54b5ed },
{ "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;
}

View File

@ -23,6 +23,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "styles/style_media_player.h"
#include "media/media_audio.h"
#include "media/player/media_player_instance.h"
#include "shortcuts.h"
namespace Media {
namespace Player {
@ -32,8 +34,33 @@ TitleButton::TitleButton(QWidget *parent) : Button(parent) {
resize(st::mediaPlayerTitleButtonSize);
setClickedCallback([this]() {
setShowPause(!_showPause);
if (exists()) {
if (_showPause) {
instance()->pause();
} else {
instance()->play();
}
}
});
if (exists()) {
subscribe(instance()->updatedNotifier(), [this](const UpdatedEvent &e) {
updatePauseState();
});
updatePauseState();
finishIconTransform();
}
}
void TitleButton::updatePauseState() {
AudioMsgId playing;
auto playbackState = audioPlayer()->currentState(&playing, AudioMsgId::Type::Song);
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;
}
setShowPause(showPause);
}
void TitleButton::setShowPause(bool showPause) {

View File

@ -25,12 +25,11 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
namespace Media {
namespace Player {
class TitleButton : public Button {
class TitleButton : public Button, private base::Subscriber {
public:
TitleButton(QWidget *parent);
void setShowPause(bool showPause);
void finishIconTransform();
void updatePauseState();
protected:
void paintEvent(QPaintEvent *e) override;
@ -40,6 +39,9 @@ protected:
private:
void paintIcon(Painter &p);
void setShowPause(bool showPause);
void finishIconTransform();
bool _showPause = true;
FloatAnimation _iconTransformToPause;
ColorAnimation _iconFg;

View File

@ -24,30 +24,112 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "ui/flatlabel.h"
#include "ui/widgets/label_simple.h"
#include "ui/buttons/icon_button.h"
#include "media/player/media_player_playback.h"
#include "media/media_audio.h"
#include "media/view/media_clip_playback.h"
#include "media/player/media_player_instance.h"
#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 {
CoverWidget::CoverWidget(QWidget *parent) : TWidget(parent)
, _nameLabel(this)
, _timeLabel(this)
, _playback(this)
, _nameLabel(this, st::mediaPlayerName)
, _timeLabel(this, st::mediaPlayerTime)
, _playback(this, st::mediaPlayerPlayback)
, _playPause(this, st::mediaPlayerPlayButton)
, _volumeController(this)
, _repeatTrack(this, st::mediaPlayerRepeatButton) {
setAttribute(Qt::WA_OpaquePaintEvent);
_playPause->setIcon(&st::mediaPlayerPauseIcon);
_playback->setChangeProgressCallback([this](float64 value) {
handleSeekProgress(value);
});
_playback->setChangeFinishedCallback([this](float64 value) {
handleSeekFinished(value);
});
if (_showPause) {
_playPause->setIcon(&st::mediaPlayerPauseIcon);
}
_playPause->setClickedCallback([this]() {
if (exists()) {
if (_showPause) {
instance()->pause();
} else {
instance()->play();
}
}
});
updateRepeatTrackIcon();
_repeatTrack->setClickedCallback([this]() {
instance()->toggleRepeat();
updateRepeatTrackIcon();
});
if (exists()) {
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));
}
}
}
void CoverWidget::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 CoverWidget::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));
}
updateTimeLabel();
if (exists()) {
instance()->stopSeeking();
}
}
void CoverWidget::resizeEvent(QResizeEvent *e) {
_nameLabel->moveToLeft(st::mediaPlayerPadding, st::mediaPlayerNameTop - st::mediaPlayerNameFont->ascent);
_timeLabel->moveToRight(st::mediaPlayerPadding, st::mediaPlayerNameTop - st::mediaPlayerTimeFont->ascent);
_playback->setGeometry(st::mediaPlayerPadding, st::mediaPlayerPlaybackTop, width() - 2 * st::mediaPlayerPadding, 2 * st::mediaPlayerPlaybackPadding + st::mediaPlayerPlaybackLine);
_nameLabel->resizeToWidth(width() - 2 * (st::mediaPlayerPadding) - _timeLabel->width() - st::normalFont->spacew);
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);
_repeatTrack->moveToRight(st::mediaPlayerPlayLeft, st::mediaPlayerPlayTop);
_volumeController->moveToRight(st::mediaPlayerVolumeRight, st::mediaPlayerPlayTop + (_playPause->height() - _volumeController->height()) / 2);
_volumeController->moveToRight(st::mediaPlayerVolumeRight, st::mediaPlayerVolumeTop);
updatePlayPrevNextPositions();
}
@ -57,7 +139,145 @@ void CoverWidget::paintEvent(QPaintEvent *e) {
}
void CoverWidget::updatePlayPrevNextPositions() {
_playPause->moveToLeft(st::mediaPlayerPlayLeft, st::mediaPlayerPlayTop);
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);
} else {
_playPause->moveToLeft(st::mediaPlayerPlayLeft, st::mediaPlayerPlayTop);
}
}
void CoverWidget::updateLabelPositions() {
_nameLabel->moveToLeft(st::mediaPlayerPadding, st::mediaPlayerNameTop - st::mediaPlayerName.font->ascent);
_timeLabel->moveToRight(st::mediaPlayerPadding, st::mediaPlayerNameTop - st::mediaPlayerTime.font->ascent);
}
void CoverWidget::updateRepeatTrackIcon() {
_repeatTrack->setIcon(instance()->repeatEnabled() ? nullptr : &st::mediaPlayerRepeatDisabledIcon);
}
void CoverWidget::handleSongUpdate(const UpdatedEvent &e) {
auto &audioId = *e.audioId;
auto &playbackState = *e.playbackState;
if (!audioId || !audioId.audio()->song()) {
return;
}
_playback->updateState(*e.playbackState);
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;
}
if (_showPause != showPause) {
_showPause = showPause;
_playPause->setIcon(_showPause ? &st::mediaPlayerPauseIcon : nullptr);
}
updateTimeText(audioId, playbackState);
}
void CoverWidget::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 (duration || !audioId.audio()->loading()) {
display = display / frequency;
_time = formatDurationText(display);
} else {
auto loaded = audioId.audio()->loadOffset();
auto loadProgress = snap(float64(loaded) / qMax(audioId.audio()->size, 1), 0., 1.);
_time = QString::number(qRound(loadProgress * 100)) + '%';
}
if (_seekPositionMs < 0) {
updateTimeLabel();
}
}
void CoverWidget::updateTimeLabel() {
auto timeLabelWidth = _timeLabel->width();
if (_seekPositionMs >= 0) {
auto playAlready = _seekPositionMs / 1000LL;
_timeLabel->setText(formatDurationText(playAlready));
} else {
_timeLabel->setText(_time);
}
if (timeLabelWidth != _timeLabel->width()) {
_nameLabel->resizeToWidth(width() - 2 * (st::mediaPlayerPadding) - _timeLabel->width() - st::normalFont->spacew);
updateLabelPositions();
}
}
void CoverWidget::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 CoverWidget::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 CoverWidget::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 CoverWidget::destroyPrevNextButtons() {
if (_previousTrack) {
_previousTrack.destroy();
_nextTrack.destroy();
updatePlayPrevNextPositions();
}
}
} // namespace Player

View File

@ -20,6 +20,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
*/
#pragma once
class AudioMsgId;
struct AudioPlaybackState;
class FlatLabel;
namespace Ui {
class LabelSimple;
@ -27,12 +29,17 @@ class IconButton;
} // namespace Ui
namespace Media {
namespace Clip {
class Playback;
} // namespace Clip
namespace Player {
class PlaybackWidget;
class VolumeController;
struct UpdatedEvent;
class CoverWidget : public TWidget {
class CoverWidget : public TWidget, private base::Subscriber {
public:
CoverWidget(QWidget *parent);
@ -41,11 +48,30 @@ protected:
void paintEvent(QPaintEvent *e) override;
private:
void handleSeekProgress(float64 progress);
void handleSeekFinished(float64 progress);
void updatePlayPrevNextPositions();
void updateLabelPositions();
void updateRepeatTrackIcon();
void createPrevNextButtons();
void destroyPrevNextButtons();
void handleSongUpdate(const UpdatedEvent &e);
void handleSongChange();
void handlePlaylistUpdate();
void updateTimeText(const AudioMsgId &audioId, const AudioPlaybackState &playbackState);
void updateTimeLabel();
bool _showPause = true;
int64 _seekPositionMs = -1;
int64 _lastDurationMs = 0;
QString _time;
ChildWidget<FlatLabel> _nameLabel;
ChildWidget<Ui::LabelSimple> _timeLabel;
ChildWidget<PlaybackWidget> _playback;
ChildWidget<Clip::Playback> _playback;
ChildWidget<Ui::IconButton> _previousTrack = { nullptr };
ChildWidget<Ui::IconButton> _playPause;
ChildWidget<Ui::IconButton> _nextTrack = { nullptr };

View File

@ -0,0 +1,249 @@
/*
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_instance.h"
#include "media/media_audio.h"
#include "observer_peer.h"
namespace Media {
namespace Player {
namespace {
Instance *SingleInstance = nullptr;
} // namespace
void start() {
audioInit();
if (audioPlayer()) {
SingleInstance = new Instance();
}
}
bool exists() {
return (audioPlayer() != nullptr);
}
void finish() {
auto temp = createAndSwap(SingleInstance);
delete temp;
audioFinish();
}
Instance::Instance() {
subscribe(audioPlayer(), [this](const AudioMsgId &audioId) {
if (audioId.type() == AudioMsgId::Type::Song) {
handleSongUpdate(audioId);
}
});
Notify::registerPeerObserver(Notify::PeerUpdate::Flag::SharedMediaChanged, this, &Instance::notifyPeerUpdated);
}
void Instance::notifyPeerUpdated(const Notify::PeerUpdate &update) {
if (!_history) {
return;
}
if (!(update.mediaTypesMask & (1 << OverviewMusicFiles))) {
return;
}
if (update.peer != _history->peer && (!_migrated || update.peer != _migrated->peer)) {
return;
}
rebuildPlaylist();
}
void Instance::handleSongUpdate(const AudioMsgId &audioId) {
emitUpdate([&audioId](const AudioMsgId &playing) {
return (audioId == playing);
});
}
void Instance::setCurrent(const AudioMsgId &audioId) {
if (_current != audioId) {
_current = audioId;
auto history = _history, migrated = _migrated;
auto item = _current ? App::histItemById(_current.contextId()) : nullptr;
if (item) {
_history = item->history()->peer->migrateTo() ? App::history(item->history()->peer->migrateTo()) : item->history();
_migrated = _history->peer->migrateFrom() ? App::history(_history->peer->migrateFrom()) : nullptr;
} else {
_history = _migrated = nullptr;
}
_songChangedNotifier.notify(true);
if (_history != history || _migrated != migrated) {
rebuildPlaylist();
}
}
}
void Instance::rebuildPlaylist() {
_playlist.clear();
if (_history && _history->loadedAtBottom()) {
auto &historyOverview = _history->overview[OverviewMusicFiles];
if (_migrated && _migrated->loadedAtBottom() && _history->loadedAtTop()) {
auto &migratedOverview = _migrated->overview[OverviewMusicFiles];
_playlist.reserve(migratedOverview.size() + historyOverview.size());
for_const (auto msgId, migratedOverview) {
_playlist.push_back(FullMsgId(_migrated->channelId(), msgId));
}
} else {
_playlist.reserve(historyOverview.size());
}
for_const (auto msgId, historyOverview) {
_playlist.push_back(FullMsgId(_history->channelId(), msgId));
}
}
_playlistChangedNotifier.notify(true);
}
void Instance::moveInPlaylist(int delta) {
auto index = _playlist.indexOf(_current.contextId());
auto newIndex = index + delta;
if (!_current || index < 0 || newIndex < 0 || newIndex >= _playlist.size()) {
rebuildPlaylist();
return;
}
auto msgId = _playlist[newIndex];
if (auto item = App::histItemById(msgId)) {
if (auto media = item->getMedia()) {
if (auto document = media->getDocument()) {
if (auto song = document->song()) {
play(AudioMsgId(document, msgId));
}
}
}
}
}
Instance *instance() {
t_assert(SingleInstance != nullptr);
return SingleInstance;
}
void Instance::play() {
AudioMsgId playing;
auto playbackState = audioPlayer()->currentState(&playing, AudioMsgId::Type::Song);
if (playing) {
if (playbackState.state & AudioPlayerStoppedMask) {
audioPlayer()->play(playing);
} else {
if (playbackState.state == AudioPlayerPausing || playbackState.state == AudioPlayerPaused || playbackState.state == AudioPlayerPausedAtEnd) {
audioPlayer()->pauseresume(AudioMsgId::Type::Song);
}
}
} else if (_current) {
audioPlayer()->play(_current);
}
}
void Instance::play(const AudioMsgId &audioId) {
if (!audioId || !audioId.audio()->song()) {
return;
}
audioPlayer()->play(audioId);
setCurrent(audioId);
if (audioId.audio()->loading()) {
documentLoadProgress(audioId.audio());
}
}
void Instance::pause() {
AudioMsgId playing;
auto playbackState = audioPlayer()->currentState(&playing, AudioMsgId::Type::Song);
if (playing) {
if (!(playbackState.state & AudioPlayerStoppedMask)) {
if (playbackState.state == AudioPlayerStarting || playbackState.state == AudioPlayerResuming || playbackState.state == AudioPlayerPlaying || playbackState.state == AudioPlayerFinishing) {
audioPlayer()->pauseresume(AudioMsgId::Type::Song);
}
}
}
}
void Instance::stop() {
audioPlayer()->stop(AudioMsgId::Type::Song);
}
void Instance::playPause() {
AudioMsgId playing;
auto playbackState = audioPlayer()->currentState(&playing, AudioMsgId::Type::Song);
if (playing) {
if (playbackState.state & AudioPlayerStoppedMask) {
audioPlayer()->play(playing);
} else {
audioPlayer()->pauseresume(AudioMsgId::Type::Song);
}
} else if (_current) {
audioPlayer()->play(_current);
}
}
void Instance::next() {
moveInPlaylist(1);
}
void Instance::previous() {
moveInPlaylist(-1);
}
void Instance::startSeeking() {
_seeking = _current;
pause();
emitUpdate([](const AudioMsgId &playing) { return true; });
}
void Instance::stopSeeking() {
_seeking = AudioMsgId();
emitUpdate([](const AudioMsgId &playing) { return true; });
}
void Instance::documentLoadProgress(DocumentData *document) {
emitUpdate([document](const AudioMsgId &audioId) {
return (audioId.audio() == document);
});
}
template <typename CheckCallback>
void Instance::emitUpdate(CheckCallback check) {
AudioMsgId playing;
auto playbackState = audioPlayer()->currentState(&playing, AudioMsgId::Type::Song);
if (!playing || !check(playing)) {
return;
}
setCurrent(playing);
_updatedNotifier.notify(UpdatedEvent(&playing, &playbackState), true);
if (_isPlaying && playbackState.state == AudioPlayerStoppedAtEnd) {
if (_repeatEnabled) {
audioPlayer()->play(_current);
} else {
next();
}
}
_isPlaying = !(playbackState.state & AudioPlayerStoppedMask);
}
} // namespace Player
} // namespace Media

View File

@ -0,0 +1,134 @@
/*
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 Notify {
struct PeerUpdate;
} // namespace Notify
struct AudioPlaybackState;
class AudioMsgId;
namespace Media {
namespace Player {
void start();
void finish();
// We use this method instead of checking for instance() != nullptr
// because audioPlayer() can be destroyed at any time by an
// error in audio playback, so we check it each time.
bool exists();
class Instance;
Instance *instance();
class Widget;
struct CreatedEvent {
explicit CreatedEvent(Widget *widget) : widget(widget) {
}
Widget *widget;
};
struct UpdatedEvent {
UpdatedEvent(const AudioMsgId *audioId, const AudioPlaybackState *playbackState) : audioId(audioId), playbackState(playbackState) {
}
const AudioMsgId *audioId;
const AudioPlaybackState *playbackState;
};
class Instance : private base::Subscriber, public Notify::Observer {
public:
void play();
void pause();
void stop();
void playPause();
void next();
void previous();
void play(const AudioMsgId &audioId);
const AudioMsgId &current() const {
return _current;
}
bool repeatEnabled() const {
return _repeatEnabled;
}
void toggleRepeat() {
_repeatEnabled = !_repeatEnabled;
}
bool isSeeking() const {
return (_seeking == _current);
}
void startSeeking();
void stopSeeking();
const QList<FullMsgId> &playlist() const {
return _playlist;
}
base::Observable<CreatedEvent> &createdNotifier() {
return _createdNotifier;
}
base::Observable<UpdatedEvent> &updatedNotifier() {
return _updatedNotifier;
}
base::Observable<void> &playlistChangedNotifier() {
return _playlistChangedNotifier;
}
base::Observable<void> &songChangedNotifier() {
return _songChangedNotifier;
}
void documentLoadProgress(DocumentData *document);
private:
Instance();
friend void start();
// Observed notifications.
void notifyPeerUpdated(const Notify::PeerUpdate &update);
void handleSongUpdate(const AudioMsgId &audioId);
void setCurrent(const AudioMsgId &audioId);
void rebuildPlaylist();
void moveInPlaylist(int delta);
template <typename CheckCallback>
void emitUpdate(CheckCallback check);
AudioMsgId _current, _seeking;
History *_history = nullptr;
History *_migrated = nullptr;
bool _repeatEnabled = false;
QList<FullMsgId> _playlist;
bool _isPlaying = false;
base::Observable<CreatedEvent> _createdNotifier;
base::Observable<UpdatedEvent> _updatedNotifier;
base::Observable<void> _playlistChangedNotifier;
base::Observable<void> _songChangedNotifier;
};
} // namespace Clip
} // namespace Media

View File

@ -1,39 +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 "media/player/media_player_playback.h"
#include "styles/style_media_player.h"
namespace Media {
namespace Player {
PlaybackWidget::PlaybackWidget(QWidget *parent) : TWidget(parent) {
}
void PlaybackWidget::paintEvent(QPaintEvent *e) {
Painter p(this);
p.fillRect(0, st::mediaPlayerPlaybackPadding, width(), st::mediaPlayerPlaybackLine, st::mediaPlayerPlaybackBg);
}
} // namespace Player
} // namespace Media

View File

@ -1,38 +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
namespace Media {
namespace Player {
class PlaybackWidget : public TWidget {
public:
PlaybackWidget(QWidget *parent);
protected:
void paintEvent(QPaintEvent *e) override;
private:
};
} // namespace Clip
} // namespace Media

View File

@ -21,31 +21,69 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "stdafx.h"
#include "media/player/media_player_volume_controller.h"
#include "media/media_audio.h"
#include "ui/buttons/icon_button.h"
#include "ui/widgets/media_slider.h"
#include "styles/style_media_player.h"
#include "styles/style_widgets.h"
namespace Media {
namespace Player {
VolumeController::VolumeController(QWidget *parent) : TWidget(parent) {
resize(st::mediaPlayerVolumeWidth, 2 * st::mediaPlayerPlaybackPadding + st::mediaPlayerPlaybackLine);
VolumeController::VolumeController(QWidget *parent) : TWidget(parent)
, _toggle(this, st::mediaPlayerVolumeToggle)
, _slider(this, st::mediaPlayerPlayback) {
_toggle->setClickedCallback([this]() {
setVolume(_slider->value() ? 0. : _rememberedVolume);
});
_slider->setChangeProgressCallback([this](float64 volume) {
applyVolumeChange(volume);
});
_slider->setChangeFinishedCallback([this](float64 volume) {
if (volume > 0) {
_rememberedVolume = volume;
}
applyVolumeChange(volume);
});
auto animated = false;
setVolume(Global::SongVolume(), animated);
resize(st::mediaPlayerVolumeWidth, 2 * st::mediaPlayerPlaybackPadding + st::mediaPlayerPlayback.width);
}
void VolumeController::paintEvent(QPaintEvent *e) {
Painter p(this);
st::mediaPlayerVolumeIcon0.paint(p, QPoint(0, (height() - st::mediaPlayerVolumeIcon0.height()) / 2), width());
auto left = rtl() ? 0 : width() - st::mediaPlayerVolumeLength;
p.fillRect(left, st::mediaPlayerPlaybackPadding, st::mediaPlayerVolumeLength, st::mediaPlayerPlaybackLine, st::mediaPlayerPlaybackBg);
void VolumeController::resizeEvent(QResizeEvent *e) {
_slider->resize(st::mediaPlayerVolumeLength, height());
_slider->moveToRight(0, 0);
_toggle->moveToLeft(0, (height() - _toggle->height()) / 2);
}
void VolumeController::mousePressEvent(QMouseEvent *e) {
void VolumeController::setVolume(float64 volume, bool animated) {
_slider->setValue(volume, animated);
if (volume > 0) {
_rememberedVolume = volume;
}
applyVolumeChange(volume);
}
void VolumeController::mouseMoveEvent(QMouseEvent *e) {
}
void VolumeController::mouseReleaseEvent(QMouseEvent *e) {
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();
}
}
}
} // namespace Player

View File

@ -20,6 +20,11 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
*/
#pragma once
namespace Ui {
class IconButton;
class MediaSlider;
} // namespace Ui
namespace Media {
namespace Player {
@ -28,12 +33,15 @@ public:
VolumeController(QWidget *parent);
protected:
void paintEvent(QPaintEvent *e) override;
void mousePressEvent(QMouseEvent *e) override;
void mouseMoveEvent(QMouseEvent *e) override;
void mouseReleaseEvent(QMouseEvent *e) override;
void resizeEvent(QResizeEvent *e) override;
private:
void setVolume(float64 volume, bool animated = true);
void applyVolumeChange(float64 volume);
ChildWidget<Ui::IconButton> _toggle;
ChildWidget<Ui::MediaSlider> _slider;
float64 _rememberedVolume = Global::kDefaultVolume;
};

View File

@ -38,7 +38,9 @@ Widget::Widget(QWidget *parent) : TWidget(parent)
_showTimer.setSingleShot(true);
connect(&_showTimer, SIGNAL(timeout()), this, SLOT(onShowStart()));
connect(_scroll, SIGNAL(scrolled()), this, SLOT(onScroll()));
if (_scroll) {
connect(_scroll, SIGNAL(scrolled()), this, SLOT(onScroll()));
}
if (cPlatform() == dbipMac || cPlatform() == dbipMacOld) {
connect(App::wnd()->windowHandle(), SIGNAL(activeChanged()), this, SLOT(onWindowActiveChanged()));

View File

@ -30,7 +30,7 @@ namespace Player {
class CoverWidget;
class ListWidget;
class Widget : public TWidget {
class Widget : public TWidget, private base::Subscriber {
Q_OBJECT
public:

View File

@ -34,7 +34,7 @@ namespace Clip {
Controller::Controller(QWidget *parent) : TWidget(parent)
, _playPauseResume(this, st::mediaviewPlayButton)
, _playback(this)
, _playback(this, st::mediaviewPlayback)
, _volumeController(this)
, _fullScreenToggle(this, st::mediaviewFullScreenButton)
, _playedAlready(this, st::mediaviewPlayProgressLabel)
@ -48,12 +48,17 @@ Controller::Controller(QWidget *parent) : TWidget(parent)
connect(_playPauseResume, SIGNAL(clicked()), this, SIGNAL(playPressed()));
connect(_fullScreenToggle, SIGNAL(clicked()), this, SIGNAL(toFullScreenPressed()));
connect(_playback, SIGNAL(seekProgress(float64)), this, SLOT(onSeekProgress(float64)));
connect(_playback, SIGNAL(seekFinished(float64)), this, SLOT(onSeekFinished(float64)));
connect(_volumeController, SIGNAL(volumeChanged(float64)), this, SIGNAL(volumeChanged(float64)));
_playback->setChangeProgressCallback([this](float64 value) {
handleSeekProgress(value);
});
_playback->setChangeFinishedCallback([this](float64 value) {
handleSeekFinished(value);
});
}
void Controller::onSeekProgress(float64 progress) {
void Controller::handleSeekProgress(float64 progress) {
if (!_lastDurationMs) return;
auto positionMs = snap(static_cast<int64>(progress * _lastDurationMs), 0LL, _lastDurationMs);
@ -64,7 +69,7 @@ void Controller::onSeekProgress(float64 progress) {
}
}
void Controller::onSeekFinished(float64 progress) {
void Controller::handleSeekFinished(float64 progress) {
if (!_lastDurationMs) return;
auto positionMs = snap(static_cast<int64>(progress * _lastDurationMs), 0LL, _lastDurationMs);
@ -99,9 +104,9 @@ void Controller::fadeUpdated(float64 opacity) {
_playback->setFadeOpacity(opacity);
}
void Controller::updatePlayback(const AudioPlaybackState &playbackState, bool reset) {
void Controller::updatePlayback(const AudioPlaybackState &playbackState) {
updatePlayPauseResumeState(playbackState);
_playback->updateState(playbackState, reset);
_playback->updateState(playbackState);
updateTimeTexts(playbackState);
}
@ -189,11 +194,13 @@ void Controller::resizeEvent(QResizeEvent *e) {
_fullScreenToggle->moveToRight(st::mediaviewFullScreenLeft, fullScreenTop);
_volumeController->moveToRight(st::mediaviewFullScreenLeft + _fullScreenToggle->width() + st::mediaviewVolumeLeft, (height() - _volumeController->height()) / 2);
_playback->resize(width() - st::mediaviewPlayPauseLeft - _playPauseResume->width() - playTop - fullScreenTop - _volumeController->width() - st::mediaviewVolumeLeft - _fullScreenToggle->width() - st::mediaviewFullScreenLeft, st::mediaviewSeekSize.height());
int playbackWidth = width() - st::mediaviewPlayPauseLeft - _playPauseResume->width() - playTop - fullScreenTop - _volumeController->width() - st::mediaviewVolumeLeft - _fullScreenToggle->width() - st::mediaviewFullScreenLeft;
_playback->resize(playbackWidth, st::mediaviewPlayback.seekSize.height());
_playback->moveToLeft(st::mediaviewPlayPauseLeft + _playPauseResume->width() + playTop, st::mediaviewPlaybackTop);
_playedAlready->moveToLeft(st::mediaviewPlayPauseLeft + _playPauseResume->width() + playTop, st::mediaviewPlayProgressTop);
_toPlayLeft->moveToRight(width() - (st::mediaviewPlayPauseLeft + _playPauseResume->width() + playTop) - _playback->width(), st::mediaviewPlayProgressTop);
_toPlayLeft->moveToRight(width() - (st::mediaviewPlayPauseLeft + _playPauseResume->width() + playTop) - playbackWidth, st::mediaviewPlayProgressTop);
}
void Controller::paintEvent(QPaintEvent *e) {

View File

@ -43,7 +43,7 @@ public:
void showAnimated();
void hideAnimated();
void updatePlayback(const AudioPlaybackState &playbackState, bool reset);
void updatePlayback(const AudioPlaybackState &playbackState);
void setInFullScreen(bool inFullScreen);
void grabStart() override;
@ -60,16 +60,15 @@ signals:
void toFullScreenPressed();
void fromFullScreenPressed();
private slots:
void onSeekProgress(float64 progress);
void onSeekFinished(float64 progress);
protected:
void resizeEvent(QResizeEvent *e) override;
void paintEvent(QPaintEvent *e) override;
void mousePressEvent(QMouseEvent *e) override;
private:
void handleSeekProgress(float64 progress);
void handleSeekFinished(float64 progress);
template <typename Callback>
void startFading(Callback start);
void fadeFinished();

View File

@ -22,17 +22,16 @@ 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) : TWidget(parent)
, _a_progress(animation(this, &Playback::step_progress)) {
setCursor(style::cur_pointer);
Playback::Playback(QWidget *parent, const style::MediaSlider &st) : _slider(new Ui::MediaSlider(parent, st)) {
}
void Playback::updateState(const AudioPlaybackState &playbackState, bool reset) {
void Playback::updateState(const AudioPlaybackState &playbackState) {
qint64 position = 0, duration = playbackState.duration;
_playing = !(playbackState.state & AudioPlayerStoppedMask);
@ -51,112 +50,11 @@ void Playback::updateState(const AudioPlaybackState &playbackState, bool reset)
progress = duration ? snap(float64(position) / duration, 0., 1.) : 0.;
}
if (duration != _duration || position != _position) {
if (duration && _duration && !reset) {
a_progress.start(progress);
_a_progress.start();
} else {
a_progress = anim::fvalue(progress, progress);
_a_progress.stop();
}
auto animated = (duration && _duration && progress > _slider->value());
_slider->setValue(progress, animated);
_position = position;
_duration = duration;
}
update();
}
void Playback::setFadeOpacity(float64 opacity) {
_fadeOpacity = opacity;
update();
}
void Playback::step_progress(float64 ms, bool timer) {
float64 dt = ms / (2 * AudioVoiceMsgUpdateView);
if (_duration && dt >= 1) {
_a_progress.stop();
a_progress.finish();
} else {
a_progress.update(qMin(dt, 1.), anim::linear);
}
if (timer) update();
}
void Playback::paintEvent(QPaintEvent *e) {
Painter p(this);
int radius = st::mediaviewPlaybackWidth / 2;
p.setOpacity(_fadeOpacity);
p.setPen(Qt::NoPen);
p.setRenderHint(QPainter::HighQualityAntialiasing);
auto ms = getms();
_a_progress.step(ms);
auto over = _a_over.current(ms, _over ? 1. : 0.);
int skip = (st::mediaviewSeekSize.width() / 2);
int length = (width() - st::mediaviewSeekSize.width());
float64 prg = _mouseDown ? _downProgress : a_progress.current();
int32 from = skip, mid = qRound(from + prg * length), end = from + length;
if (mid > from) {
p.setClipRect(0, 0, mid, height());
p.setOpacity(_fadeOpacity * (over * st::mediaviewActiveOpacity + (1. - over) * st::mediaviewInactiveOpacity));
p.setBrush(st::mediaviewPlaybackActive);
p.drawRoundedRect(0, (height() - st::mediaviewPlaybackWidth) / 2, mid + radius, st::mediaviewPlaybackWidth, radius, radius);
}
if (end > mid) {
p.setClipRect(mid, 0, width() - mid, height());
p.setOpacity(_fadeOpacity);
p.setBrush(st::mediaviewPlaybackInactive);
p.drawRoundedRect(mid - radius, (height() - st::mediaviewPlaybackWidth) / 2, width() - (mid - radius), st::mediaviewPlaybackWidth, radius, radius);
}
if (over > 0) {
int x = mid - skip;
p.setClipRect(rect());
p.setOpacity(_fadeOpacity * st::mediaviewActiveOpacity);
auto seekButton = QRect(x, (height() - st::mediaviewSeekSize.height()) / 2, st::mediaviewSeekSize.width(), st::mediaviewSeekSize.height());
int remove = ((1. - over) * st::mediaviewSeekSize.width()) / 2.;
if (remove * 2 < st::mediaviewSeekSize.width()) {
p.setBrush(st::mediaviewPlaybackActive);
p.drawEllipse(seekButton.marginsRemoved(QMargins(remove, remove, remove, remove)));
}
}
}
void Playback::mouseMoveEvent(QMouseEvent *e) {
if (_mouseDown) {
_downProgress = snap(e->pos().x() / float64(width()), 0., 1.);
emit seekProgress(_downProgress);
update();
}
}
void Playback::mousePressEvent(QMouseEvent *e) {
_mouseDown = true;
_downProgress = snap(e->pos().x() / float64(width()), 0., 1.);
update();
emit seekProgress(_downProgress); // This may destroy Playback.
}
void Playback::mouseReleaseEvent(QMouseEvent *e) {
if (_mouseDown) {
_mouseDown = false;
emit seekFinished(_downProgress);
update();
}
}
void Playback::enterEvent(QEvent *e) {
setOver(true);
}
void Playback::leaveEvent(QEvent *e) {
setOver(false);
}
void Playback::setOver(bool over) {
if (_over == over) return;
_over = over;
auto from = _over ? 0. : 1., to = _over ? 1. : 0.;
START_ANIMATION(_a_over, func(this, &Playback::updateCallback), from, to, st::mediaviewOverDuration, anim::linear);
}
} // namespace Clip

View File

@ -20,51 +20,56 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
*/
#pragma once
#include "ui/widgets/media_slider.h"
struct AudioPlaybackState;
namespace style {
struct MediaSlider;
} // namespace style
namespace Ui {
class MediaSlider;
} // namespace Ui
namespace Media {
namespace Clip {
class Playback : public TWidget {
Q_OBJECT
class Playback {
public:
Playback(QWidget *parent);
Playback(QWidget *parent, const style::MediaSlider &st);
void updateState(const AudioPlaybackState &playbackState, bool reset);
void setFadeOpacity(float64 opacity);
void updateState(const AudioPlaybackState &playbackState);
signals:
void seekProgress(float64 progress);
void seekFinished(float64 progress);
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;
void setFadeOpacity(float64 opacity) {
_slider->setFadeOpacity(opacity);
}
void setChangeProgressCallback(Ui::MediaSlider::Callback &&callback) {
_slider->setChangeProgressCallback(std_::move(callback));
}
void setChangeFinishedCallback(Ui::MediaSlider::Callback &&callback) {
_slider->setChangeFinishedCallback(std_::move(callback));
}
void setGeometry(int x, int y, int w, int h) {
_slider->setGeometry(x, y, w, h);
}
void hide() {
_slider->hide();
}
void show() {
_slider->show();
}
void moveToLeft(int x, int y) {
_slider->moveToLeft(x, y);
}
void resize(int w, int h) {
_slider->resize(w, h);
}
private:
void step_progress(float64 ms, bool timer);
void updateCallback() {
update();
}
void setOver(bool over);
bool _over = false;
FloatAnimation _a_over;
Ui::MediaSlider *_slider;
int64 _position = 0;
int64 _duration = 0;
anim::fvalue a_progress = { 0., 0. };
Animation _a_progress;
bool _mouseDown = false;
float64 _downProgress = 0.;
float64 _fadeOpacity = 1.;
bool _playing = false;
};

View File

@ -26,6 +26,18 @@ mediaviewActiveOpacity: 1.;
mediaviewInactiveOpacity: 0.78;
mediaviewOverDuration: 150;
mediaviewPlaybackActive: #ffffff;
mediaviewPlaybackInactive: #474747;
mediaviewPlayback: MediaSlider {
width: 3px;
activeFg: mediaviewPlaybackActive;
inactiveFg: mediaviewPlaybackInactive;
activeOpacity: mediaviewActiveOpacity;
inactiveOpacity: mediaviewInactiveOpacity;
seekSize: size(11px, 11px);
duration: mediaviewOverDuration;
}
mediaviewControllerSize: size(600px, 50px);
mediaviewPlayProgressLabel: LabelSimple(defaultLabelSimple) {
font: semiboldFont;
@ -65,11 +77,7 @@ mediaviewFullScreenOutIcon: icon {
{ "media_fullscreen_from", #ffffff, point(0px, 0px) },
};
mediaviewPlaybackActive: #ffffff;
mediaviewPlaybackInactive: #474747;
mediaviewPlaybackWidth: 3px;
mediaviewPlaybackTop: 28px;
mediaviewSeekSize: size(11px, 11px);
mediaviewVolumeSize: size(44px, 20px);
mediaviewVolumeIcon: icon {

View File

@ -1450,7 +1450,7 @@ void MediaView::restartVideoAtSeekPosition(int64 positionMs) {
state.position = _videoPositionMs;
state.duration = _videoDurationMs;
state.frequency = _videoFrequencyMs;
updateVideoPlaybackState(state, true);
updateVideoPlaybackState(state);
}
void MediaView::onVideoSeekProgress(int64 positionMs) {
@ -1497,12 +1497,12 @@ void MediaView::onVideoPlayProgress(const AudioMsgId &audioId) {
}
}
void MediaView::updateVideoPlaybackState(const AudioPlaybackState &state, bool reset) {
void MediaView::updateVideoPlaybackState(const AudioPlaybackState &state) {
if (state.frequency) {
if (state.state & AudioPlayerStoppedMask) {
_videoStopped = true;
}
_clipController->updatePlayback(state, reset);
_clipController->updatePlayback(state);
} else { // Audio has stopped already.
_videoIsSilent = true;
updateSilentVideoPlaybackState();

View File

@ -135,7 +135,7 @@ private:
void updateCursor();
void setZoomLevel(int newZoom);
void updateVideoPlaybackState(const AudioPlaybackState &state, bool reset = false);
void updateVideoPlaybackState(const AudioPlaybackState &state);
void updateSilentVideoPlaybackState();
void restartVideoAtSeekPosition(int64 positionMs);

View File

@ -22,7 +22,7 @@ import glob
import re
import binascii
# define some checked flag convertions
# define some checked flag conversions
# the key flag type should be a subset of the value flag type
# with exact the same names, then the key flag can be implicitly
# casted to the value flag type

View File

@ -32,6 +32,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "mainwindow.h"
#include "playerwidget.h"
#include "media/media_audio.h"
#include "media/player/media_player_instance.h"
#include "localstorage.h"
namespace Overview {
@ -869,7 +870,7 @@ bool Document::updateStatusText() const {
realDuration = playbackState.duration / (playbackState.frequency ? playbackState.frequency : AudioVoiceMsgFrequency);
showPause = (playbackState.state == AudioPlayerPlaying || playbackState.state == AudioPlayerResuming || playbackState.state == AudioPlayerStarting);
}
if (!showPause && (playing == AudioMsgId(_data, _parent->fullId())) && App::main() && App::main()->player()->seekingSong(playing)) {
if (!showPause && (playing == AudioMsgId(_data, _parent->fullId())) && Media::Player::exists() && Media::Player::instance()->isSeeking()) {
showPause = true;
}
}

View File

@ -1922,7 +1922,7 @@ 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&)));
// connect(App::main()->player(), SIGNAL(playerSongChanged(const FullMsgId&)), this, SLOT(onPlayerSongChanged(const FullMsgId&)));
switchType(type);
}
@ -2262,14 +2262,14 @@ void OverviewWidget::onScrollTimer() {
_scroll.scrollToY(_scroll.scrollTop() + d);
}
void OverviewWidget::onPlayerSongChanged(const FullMsgId &msgId) {
if (type() == OverviewMusicFiles) {
//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()) {

View File

@ -334,7 +334,7 @@ public slots:
void onScroll();
void onScrollTimer();
void onPlayerSongChanged(const FullMsgId &msgId);
// void onPlayerSongChanged(const FullMsgId &msgId);
void onForwardSelected();
void onDeleteSelected();

View File

@ -469,7 +469,7 @@ void PlayerWidget::playPressed() {
}
} else {
audioPlayer()->play(_song);
if (App::main()) App::main()->audioPlayProgress(_song);
audioPlayer()->notify(_song);
}
}
@ -494,15 +494,15 @@ void PlayerWidget::playPausePressed() {
audioPlayer()->pauseresume(AudioMsgId::Type::Song);
} else {
audioPlayer()->play(_song);
if (App::main()) App::main()->audioPlayProgress(_song);
audioPlayer()->notify(_song);
}
}
void PlayerWidget::prevPressed() {
if (isHidden()) return;
History *history = _msgmigrated ? _migrated : _history;
const History::MediaOverview *o = history ? &history->overview[OverviewMusicFiles] : 0;
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) {
@ -516,8 +516,8 @@ void PlayerWidget::prevPressed() {
void PlayerWidget::nextPressed() {
if (isHidden()) return;
History *history = _msgmigrated ? _migrated : _history;
const History::MediaOverview *o = history ? &history->overview[OverviewMusicFiles] : 0;
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)) {
@ -680,11 +680,11 @@ void PlayerWidget::updateState(AudioMsgId playing, AudioPlaybackState playbackSt
if (wasPlaying && playbackState.state == AudioPlayerStoppedAtEnd) {
if (_repeat) {
if (_song.audio()) {
audioPlayer()->play(_song, OverviewMusicFiles);
updateState();
// audioPlayer()->play(_song);
// updateState();
}
} else {
nextPressed();
// nextPressed();
}
}

View File

@ -23,6 +23,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "application.h"
#include "playerwidget.h"
#include "localstorage.h"
#include "media/player/media_player_instance.h"
#include "lang.h"
@ -336,21 +337,27 @@ bool PsMacWindowPrivate::filterNativeEvent(void *event) {
switch (keyCode) {
case NX_KEYTYPE_PLAY:
if (keyState == 0) { // Play pressed and released
if (App::main()) App::main()->player()->playPausePressed();
if (Media::Player::exists()) {
Media::Player::instance()->playPause();
}
return true;
}
break;
case NX_KEYTYPE_FAST:
if (keyState == 0) { // Next pressed and released
if (App::main()) App::main()->player()->nextPressed();
if (Media::Player::exists()) {
Media::Player::instance()->next();
}
return true;
}
break;
case NX_KEYTYPE_REWIND:
if (keyState == 0) { // Previous pressed and released
if (App::main()) App::main()->player()->prevPressed();
if (Media::Player::exists()) {
Media::Player::instance()->previous();
}
return true;
}
break;

View File

@ -51,7 +51,7 @@ private:
};
class DownloadPathState : public TWidget, public base::Subscriber {
class DownloadPathState : public TWidget, private base::Subscriber {
Q_OBJECT
public:

View File

@ -25,7 +25,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
namespace Settings {
class LocalPasscodeState : public TWidget, public base::Subscriber {
class LocalPasscodeState : public TWidget, private base::Subscriber {
Q_OBJECT
public:

View File

@ -24,7 +24,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "mainwindow.h"
#include "passcodewidget.h"
#include "mainwidget.h"
#include "playerwidget.h"
#include "media/player/media_player_instance.h"
namespace ShortcutCommands {
@ -77,61 +77,49 @@ bool quit_telegram() {
//}
bool media_play() {
if (auto m = App::main()) {
if (!m->player()->isHidden()) {
m->player()->playPressed();
return true;
}
if (Media::Player::exists()) {
Media::Player::instance()->play();
return true;
}
return false;
}
bool media_pause() {
if (auto m = App::main()) {
if (!m->player()->isHidden()) {
m->player()->pausePressed();
return true;
}
if (Media::Player::exists()) {
Media::Player::instance()->pause();
return true;
}
return false;
}
bool media_playpause() {
if (auto m = App::main()) {
if (!m->player()->isHidden()) {
m->player()->playPausePressed();
return true;
}
if (Media::Player::exists()) {
Media::Player::instance()->playPause();
return true;
}
return false;
}
bool media_stop() {
if (auto m = App::main()) {
if (!m->player()->isHidden()) {
m->player()->stopPressed();
return true;
}
if (Media::Player::exists()) {
Media::Player::instance()->stop();
return true;
}
return false;
}
bool media_previous() {
if (auto m = App::main()) {
if (!m->player()->isHidden()) {
m->player()->prevPressed();
return true;
}
if (Media::Player::exists()) {
Media::Player::instance()->previous();
return true;
}
return false;
}
bool media_next() {
if (auto m = App::main()) {
if (!m->player()->isHidden()) {
m->player()->nextPressed();
return true;
}
if (Media::Player::exists()) {
Media::Player::instance()->next();
return true;
}
return false;
}

View File

@ -774,20 +774,20 @@ void PhotoOpenClickHandler::onClickImpl() const {
}
void PhotoSaveClickHandler::onClickImpl() const {
PhotoData *data = photo();
auto data = photo();
if (!data->date) return;
data->download();
}
void PhotoCancelClickHandler::onClickImpl() const {
PhotoData *data = photo();
auto data = photo();
if (!data->date) return;
if (data->uploading()) {
if (HistoryItem *item = App::hoveredLinkItem() ? App::hoveredLinkItem() : (App::contextItem() ? App::contextItem() : 0)) {
if (HistoryMessage *msg = item->toHistoryMessage()) {
if (msg->getMedia() && msg->getMedia()->type() == MediaTypePhoto && static_cast<HistoryPhoto*>(msg->getMedia())->photo() == data) {
if (auto item = App::hoveredLinkItem() ? App::hoveredLinkItem() : (App::contextItem() ? App::contextItem() : nullptr)) {
if (auto media = item->getMedia()) {
if (media->type() == MediaTypePhoto && static_cast<HistoryPhoto*>(media)->photo() == data) {
App::contextItem(item);
App::main()->deleteLayer(-2);
}
@ -975,8 +975,8 @@ void DocumentOpenClickHandler::doOpen(DocumentData *data, ActionOnLoad action) {
} else {
AudioMsgId audio(data, msgId);
audioPlayer()->play(audio);
audioPlayer()->notify(audio);
if (App::main()) {
App::main()->audioPlayProgress(audio);
App::main()->mediaMarkRead(data);
}
}
@ -988,7 +988,7 @@ void DocumentOpenClickHandler::doOpen(DocumentData *data, ActionOnLoad action) {
} else {
AudioMsgId song(data, msgId);
audioPlayer()->play(song);
if (App::main()) App::main()->audioPlayProgress(song);
audioPlayer()->notify(song);
}
} else if (playVideo) {
if (!data->data().isEmpty()) {
@ -1084,13 +1084,13 @@ void DocumentSaveClickHandler::onClickImpl() const {
}
void DocumentCancelClickHandler::onClickImpl() const {
DocumentData *data = document();
auto data = document();
if (!data->date) return;
if (data->uploading()) {
if (HistoryItem *item = App::hoveredLinkItem() ? App::hoveredLinkItem() : (App::contextItem() ? App::contextItem() : 0)) {
if (HistoryMessage *msg = item->toHistoryMessage()) {
if (msg->getMedia() && msg->getMedia()->getDocument() == data) {
if (auto item = App::hoveredLinkItem() ? App::hoveredLinkItem() : (App::contextItem() ? App::contextItem() : nullptr)) {
if (auto media = item->getMedia()) {
if (media->getDocument() == data) {
App::contextItem(item);
App::main()->deleteLayer(-2);
}
@ -1270,7 +1270,7 @@ void DocumentData::performActionOnLoad() {
auto playbackState = audioPlayer()->currentState(&playing, AudioMsgId::Type::Voice);
if (playing == AudioMsgId(this, _actionOnLoadMsgId) && !(playbackState.state & AudioPlayerStoppedMask) && playbackState.state != AudioPlayerFinishing) {
audioPlayer()->pauseresume(AudioMsgId::Type::Voice);
} else {
} else if (playbackState.state & AudioPlayerStoppedMask) {
audioPlayer()->play(AudioMsgId(this, _actionOnLoadMsgId));
if (App::main()) App::main()->mediaMarkRead(this);
}
@ -1281,10 +1281,10 @@ void DocumentData::performActionOnLoad() {
auto playbackState = audioPlayer()->currentState(&playing, AudioMsgId::Type::Song);
if (playing == AudioMsgId(this, _actionOnLoadMsgId) && !(playbackState.state & AudioPlayerStoppedMask) && playbackState.state != AudioPlayerFinishing) {
audioPlayer()->pauseresume(AudioMsgId::Type::Song);
} else {
} else if (playbackState.state & AudioPlayerStoppedMask) {
AudioMsgId song(this, _actionOnLoadMsgId);
audioPlayer()->play(song);
if (App::main()) App::main()->audioPlayProgress(song);
audioPlayer()->notify(song);
}
}
} else if (playAnimation) {
@ -1428,15 +1428,17 @@ void DocumentData::save(const QString &toFile, ActionOnLoad action, const FullMs
void DocumentData::cancel() {
if (!loading()) return;
FileLoader *l = _loader;
auto loader = createAndSwap(_loader);
_loader = CancelledMtpFileLoader;
if (l) {
l->cancel();
l->deleteLater();
l->stop();
loader->cancel();
loader->deleteLater();
loader->stop();
notifyLayoutChanged();
notifyLayoutChanged();
if (auto main = App::main()) {
main->documentLoadProgress(this);
}
_actionOnLoad = ActionOnLoadNone;
}

View File

@ -1126,13 +1126,13 @@ public:
return (type == SongDocument) ? static_cast<SongData*>(_additional.get()) : nullptr;
}
const SongData *song() const {
return (type == SongDocument) ? static_cast<const SongData*>(_additional.get()) : nullptr;
return const_cast<DocumentData*>(this)->song();
}
VoiceData *voice() {
return (type == VoiceDocument) ? static_cast<VoiceData*>(_additional.get()) : nullptr;
}
const VoiceData *voice() const {
return (type == VoiceDocument) ? static_cast<const VoiceData*>(_additional.get()) : nullptr;
return const_cast<DocumentData*>(this)->voice();
}
bool isAnimation() const {
return (type == AnimatedDocument) || !mime.compare(qstr("image/gif"), Qt::CaseInsensitive);
@ -1141,7 +1141,10 @@ public:
return (type == AnimatedDocument) && !mime.compare(qstr("video/mp4"), Qt::CaseInsensitive);
}
bool isMusic() const {
return (type == SongDocument) ? !static_cast<SongData*>(_additional.get())->title.isEmpty() : false;
if (auto s = song()) {
return (s->duration > 0);
}
return false;
}
bool isVideo() const {
return (type == VideoDocument);

View File

@ -29,6 +29,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "boxes/aboutbox.h"
#include "media/media_audio.h"
#include "media/player/media_player_button.h"
#include "media/player/media_player_widget.h"
#include "media/player/media_player_instance.h"
class TitleWidget::Hider : public TWidget {
public:
@ -100,10 +102,12 @@ TitleWidget::TitleWidget(QWidget *parent) : TWidget(parent)
#endif
subscribe(Adaptive::Changed(), [this]() { updateAdaptiveLayout(); });
if (auto player = audioPlayer()) {
subscribe(player, [this](const AudioMsgId &audio) {
if (audio.type() == AudioMsgId::Type::Song) {
handleSongUpdate(audio);
if (Media::Player::exists()) {
subscribe(Media::Player::instance()->createdNotifier(), [this](const Media::Player::CreatedEvent &e) {
if (!_player) {
_player.create(this);
_player->installEventFilter(e.widget);
updateControlsVisibility();
}
});
}
@ -157,20 +161,6 @@ void TitleWidget::setHideLevel(float64 level) {
}
}
void TitleWidget::handleSongUpdate(const AudioMsgId &audioId) {
t_assert(audioId.type() == AudioMsgId::Type::Song);
AudioMsgId playing;
auto playbackState = audioPlayer()->currentState(&playing, audioId.type());
if (playing == audioId) {
auto songIsPlaying = !(playbackState.state & AudioPlayerStoppedMask) && (playbackState.state != AudioPlayerFinishing);
if (songIsPlaying && !_player) {
_player.create(this);
updateControlsVisibility();
}
}
}
void TitleWidget::onContacts() {
if (App::wnd() && App::wnd()->isHidden()) App::wnd()->showFromTray();

View File

@ -27,6 +27,7 @@ class MainWindow;
namespace Media {
namespace Player {
class TitleButton;
class CreatedEvent;
} // namespace Player
} // namespace Media
class AudioMsgId;
@ -44,10 +45,6 @@ public:
void maximizedChanged(bool maximized, bool force = false);
Media::Player::TitleButton *playerButton() {
return _player;
}
HitTestType hitTest(const QPoint &p);
void setHideLevel(float64 level);
@ -74,7 +71,7 @@ private:
void updateSystemButtonsVisibility();
void updateControlsPosition();
void handleSongUpdate(const AudioMsgId &audioId);
void handleMediaPlayerCreated(const Media::Player::CreatedEvent &e);
style::color statusColor;

View File

@ -32,7 +32,7 @@ namespace {
Qt::LayoutDirectionAuto, // dir
};
TextParseOptions _labelMarkedOptions = {
TextParseMultiline | TextParseRichText | TextParseLinks | TextParseHashtags | TextParseMentions | TextParseBotCommands, // flags
TextParseMultiline | TextParseRichText | TextParseLinks | TextParseHashtags | TextParseMentions | TextParseBotCommands | TextParseMono, // flags
0, // maxw
0, // maxh
Qt::LayoutDirectionAuto, // dir
@ -67,28 +67,32 @@ void FlatLabel::init() {
connect(&_touchSelectTimer, SIGNAL(timeout()), this, SLOT(onTouchSelect()));
}
void FlatLabel::setText(const QString &text) {
template <typename SetCallback>
void FlatLabel::setTextByCallback(SetCallback callback) {
textstyleSet(&_tst);
_text.setText(_st.font, text, _labelOptions);
callback();
refreshSize();
textstyleRestore();
setMouseTracking(_selectable || _text.hasLinks());
update();
}
void FlatLabel::setText(const QString &text) {
setTextByCallback([this, &text]() {
_text.setText(_st.font, text, _labelOptions);
});
}
void FlatLabel::setRichText(const QString &text) {
textstyleSet(&_tst);
_text.setRichText(_st.font, text, _labelOptions);
refreshSize();
textstyleRestore();
setMouseTracking(_selectable || _text.hasLinks());
setTextByCallback([this, &text]() {
_text.setRichText(_st.font, text, _labelOptions);
});
}
void FlatLabel::setMarkedText(const TextWithEntities &textWithEntities) {
textstyleSet(&_tst);
_text.setMarkedText(_st.font, textWithEntities, _labelMarkedOptions);
refreshSize();
textstyleRestore();
setMouseTracking(_selectable || _text.hasLinks());
setTextByCallback([this, &textWithEntities]() {
_text.setMarkedText(_st.font, textWithEntities, _labelMarkedOptions);
});
}
void FlatLabel::setSelectable(bool selectable) {

View File

@ -84,6 +84,9 @@ private slots:
private:
void init();
template <typename SetCallback>
void setTextByCallback(SetCallback callback);
Text::StateResult dragActionUpdate();
Text::StateResult dragActionStart(const QPoint &p, Qt::MouseButton button);
Text::StateResult dragActionFinish(const QPoint &p, Qt::MouseButton button);

View File

@ -0,0 +1,164 @@
/*
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/media_slider.h"
#include "styles/style_widgets.h"
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);
}
float64 MediaSlider::value() const {
return a_value.current();
}
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());
}
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 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 = qRound(from + prg * length), 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);
}
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);
}
if (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()) {
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.;
START_ANIMATION(_a_over, func([this]() { update(); }), from, to, _st.duration, anim::linear);
}
} // namespace Ui

View File

@ -0,0 +1,79 @@
/*
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 style {
struct MediaSlider;
} // namespace style
namespace Ui {
class MediaSlider : public TWidget {
public:
MediaSlider(QWidget *parent, const style::MediaSlider &st);
float64 value() const;
void setValue(float64 value, bool animated);
void setFadeOpacity(float64 opacity);
using Callback = base::lambda_unique<void(float64)>;
void setChangeProgressCallback(Callback &&callback) {
_changeProgressCallback = std_::move(callback);
}
void setChangeFinishedCallback(Callback &&callback) {
_changeFinishedCallback = std_::move(callback);
}
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;
const style::MediaSlider &_st;
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

@ -33,4 +33,14 @@ defaultLabelSimple: LabelSimple {
textFg: windowTextFg;
}
MediaSlider {
width: pixels;
activeFg: color;
inactiveFg: color;
activeOpacity: double;
inactiveOpacity: double;
seekSize: size;
duration: int;
}
widgetSlideDuration: 200;

View File

@ -245,10 +245,10 @@
'<(src_loc)/media/player/media_player_button.h',
'<(src_loc)/media/player/media_player_cover.cpp',
'<(src_loc)/media/player/media_player_cover.h',
'<(src_loc)/media/player/media_player_instance.cpp',
'<(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_playback.cpp',
'<(src_loc)/media/player/media_player_playback.h',
'<(src_loc)/media/player/media_player_volume_controller.cpp',
'<(src_loc)/media/player/media_player_volume_controller.h',
'<(src_loc)/media/player/media_player_widget.cpp',
@ -433,6 +433,8 @@
'<(src_loc)/ui/toast/toast_widget.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/widget_slide_wrap.h',
'<(src_loc)/ui/animation.cpp',
'<(src_loc)/ui/animation.h',

View File

@ -117,6 +117,7 @@
'<(PRODUCT_DIR)/codegen_style<(exe_ext)',
'-I<(res_loc)', '-I<(src_loc)', '--skip-sprites',
'-o<(SHARED_INTERMEDIATE_DIR)/styles',
'-w<(PRODUCT_DIR)/../..',
# GYP/Ninja bug workaround: if we specify just <(RULE_INPUT_PATH)
# the <(RULE_INPUT_ROOT) variables won't be available in Ninja,