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.
|
@ -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;
|
||||
|
|
BIN
Telegram/Resources/icons/player_close.png
Normal file
After Width: | Height: | Size: 133 B |
BIN
Telegram/Resources/icons/player_close@2x.png
Normal file
After Width: | Height: | Size: 197 B |
Before Width: | Height: | Size: 188 B After Width: | Height: | Size: 219 B |
Before Width: | Height: | Size: 297 B After Width: | Height: | Size: 319 B |
BIN
Telegram/Resources/icons/player_panel_next.png
Normal file
After Width: | Height: | Size: 188 B |
BIN
Telegram/Resources/icons/player_panel_next@2x.png
Normal file
After Width: | Height: | Size: 297 B |
BIN
Telegram/Resources/icons/player_panel_pin.png
Normal file
After Width: | Height: | Size: 349 B |
BIN
Telegram/Resources/icons/player_panel_pin@2x.png
Normal file
After Width: | Height: | Size: 547 B |
BIN
Telegram/Resources/icons/player_panel_previous.png
Normal file
After Width: | Height: | Size: 182 B |
BIN
Telegram/Resources/icons/player_panel_previous@2x.png
Normal file
After Width: | Height: | Size: 292 B |
Before Width: | Height: | Size: 182 B After Width: | Height: | Size: 201 B |
Before Width: | Height: | Size: 292 B After Width: | Height: | Size: 325 B |
Before Width: | Height: | Size: 299 B After Width: | Height: | Size: 264 B |
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -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)));
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -93,7 +93,7 @@ public:
|
|||
if (!_active || !*_active) {
|
||||
return;
|
||||
}
|
||||
_pressed.makeIfNull();
|
||||
_pressed.createIfNull();
|
||||
*_pressed = *_active;
|
||||
if ((_pressedHost = _activeHost)) {
|
||||
_pressedHost->clickHandlerPressedChanged(*_pressed, true);
|
||||
|
|
|
@ -189,7 +189,10 @@ public:
|
|||
std::swap(_p, other._p);
|
||||
}
|
||||
~unique_ptr() noexcept {
|
||||
delete _p;
|
||||
if (_p) {
|
||||
delete _p;
|
||||
_p = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
T &operator*() const {
|
||||
|
|
|
@ -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)...));
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ NeverFreedPointer<DataStructures> structures;
|
|||
namespace internal {
|
||||
|
||||
void registerAbstractStructure(AbstractStructure **p) {
|
||||
structures.makeIfNull();
|
||||
structures.createIfNull();
|
||||
structures->insert(p);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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;
|
||||
|
||||
};
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -209,7 +209,7 @@ const DocumentItems *documentItems() {
|
|||
namespace internal {
|
||||
|
||||
void regDocumentItem(DocumentData *document, ItemBase *item) {
|
||||
documentItemsMap.makeIfNull();
|
||||
documentItemsMap.createIfNull();
|
||||
(*documentItemsMap)[document].insert(item);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 ¶ms) {
|
||||
_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));
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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()));
|
||||
|
|
|
@ -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;
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
};
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
};
|
||||
|
||||
|
|
225
Telegram/SourceFiles/media/player/media_player_panel.cpp
Normal 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
|
94
Telegram/SourceFiles/media/player/media_player_panel.h
Normal 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
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -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 ¤t = 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 ¤t = 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
|
||||
|
|
|
@ -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;
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -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!"));
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -96,7 +96,7 @@ namespace Notifications {
|
|||
|
||||
void start() {
|
||||
if (cPlatform() != dbipMacOld) {
|
||||
ManagerInstance.makeIfNull();
|
||||
ManagerInstance.createIfNull();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -316,7 +316,7 @@ private:
|
|||
|
||||
void start() {
|
||||
if (init()) {
|
||||
ManagerInstance.makeIfNull();
|
||||
ManagerInstance.createIfNull();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
||||
};
|
|
@ -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() {
|
||||
|
|
|
@ -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;
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
});
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -46,7 +46,7 @@ void stopModules() {
|
|||
} // namespace
|
||||
|
||||
void registerModule(ModuleBase *module) {
|
||||
styleModules.makeIfNull();
|
||||
styleModules.createIfNull();
|
||||
styleModules->push_back(module);
|
||||
}
|
||||
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
129
Telegram/SourceFiles/ui/widgets/continuous_slider.cpp
Normal 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
|
109
Telegram/SourceFiles/ui/widgets/continuous_slider.h
Normal 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
|
73
Telegram/SourceFiles/ui/widgets/filled_slider.cpp
Normal 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
|
46
Telegram/SourceFiles/ui/widgets/filled_slider.h
Normal 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
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
};
|
||||
|
||||
|
|
56
Telegram/SourceFiles/ui/widgets/shadow.cpp
Normal 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
|
47
Telegram/SourceFiles/ui/widgets/shadow.h
Normal 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
|
|
@ -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;
|
||||
|
|
|
@ -43,6 +43,14 @@ MediaSlider {
|
|||
duration: int;
|
||||
}
|
||||
|
||||
FilledSlider {
|
||||
fullWidth: pixels;
|
||||
lineWidth: pixels;
|
||||
activeFg: color;
|
||||
inactiveFg: color;
|
||||
duration: int;
|
||||
}
|
||||
|
||||
widgetSlideDuration: 200;
|
||||
|
||||
discreteSliderHeight: 39px;
|
||||
|
|
|
@ -83,7 +83,7 @@ void ChatBackground::setTile(bool tile) {
|
|||
}
|
||||
|
||||
ChatBackground *chatBackground() {
|
||||
instance.makeIfNull();
|
||||
instance.createIfNull();
|
||||
return instance.data();
|
||||
}
|
||||
|
||||
|
|
|
@ -57,7 +57,7 @@ internal::Widget::Direction notificationShiftDirection() {
|
|||
} // namespace
|
||||
|
||||
void start() {
|
||||
ManagerInstance.makeIfNull();
|
||||
ManagerInstance.createIfNull();
|
||||
}
|
||||
|
||||
Manager *manager() {
|
||||
|
|
44
Telegram/SourceFiles/window/player_wrap_widget.cpp
Normal 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
|
40
Telegram/SourceFiles/window/player_wrap_widget.h
Normal 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
|
|
@ -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',
|
||||
|
|