From a8f3582cb11e666daef5b06ddccfcaee0bf59c7a Mon Sep 17 00:00:00 2001
From: John Preston <johnprestonmail@gmail.com>
Date: Wed, 21 Sep 2016 14:44:20 +0300
Subject: [PATCH] Title controls reordering done right. Title song play button
 created.

---
 Telegram/SourceFiles/app.cpp                  |   7 +-
 Telegram/SourceFiles/boxes/passcodebox.cpp    |   2 +-
 Telegram/SourceFiles/historywidget.cpp        |  10 +-
 Telegram/SourceFiles/mainwidget.cpp           |  39 +-
 Telegram/SourceFiles/mainwidget.h             |   5 -
 Telegram/SourceFiles/mainwindow.cpp           |  31 +-
 Telegram/SourceFiles/mainwindow.h             |  16 +-
 Telegram/SourceFiles/media/media_audio.cpp    |  28 +-
 Telegram/SourceFiles/media/media_audio.h      |  33 +-
 Telegram/SourceFiles/mtproto/facade.cpp       |   2 +-
 Telegram/SourceFiles/mtproto/facade.h         |   2 +-
 .../settings/settings_scale_widget.cpp        |   2 +-
 Telegram/SourceFiles/sysbuttons.cpp           |  79 ++--
 Telegram/SourceFiles/sysbuttons.h             |  82 +----
 Telegram/SourceFiles/title.cpp                | 341 ++++++++++--------
 Telegram/SourceFiles/title.h                  |  42 +--
 Telegram/SourceFiles/ui/twidget.h             |   6 +
 17 files changed, 320 insertions(+), 407 deletions(-)

diff --git a/Telegram/SourceFiles/app.cpp b/Telegram/SourceFiles/app.cpp
index eedca3a78..d1c5caeb9 100644
--- a/Telegram/SourceFiles/app.cpp
+++ b/Telegram/SourceFiles/app.cpp
@@ -200,7 +200,7 @@ namespace {
 			w->notifyClearFast();
 			w->setupIntro(true);
 		}
-		MTP::authed(0);
+		MTP::setAuthedId(0);
 		Local::reset();
 
 		cSetOtherOnline(0);
@@ -211,9 +211,8 @@ namespace {
 		if (App::uploader()) App::uploader()->clear();
 		clearStorageImages();
 		if (auto w = wnd()) {
-			w->getTitle()->updateBackButton();
-			w->updateTitleStatus();
-			w->getTitle()->resizeEvent(0);
+			w->updateConnectingStatus();
+			w->getTitle()->updateControlsVisibility();
 		}
 		return true;
 	}
diff --git a/Telegram/SourceFiles/boxes/passcodebox.cpp b/Telegram/SourceFiles/boxes/passcodebox.cpp
index 32ccd9e8a..d0ef9b754 100644
--- a/Telegram/SourceFiles/boxes/passcodebox.cpp
+++ b/Telegram/SourceFiles/boxes/passcodebox.cpp
@@ -406,7 +406,7 @@ void PasscodeBox::onSave(bool force) {
 		cSetPasscodeBadTries(0);
 		Local::setPasscode(pwd.toUtf8());
 		App::wnd()->checkAutoLock();
-		App::wnd()->getTitle()->showUpdateBtn();
+		App::wnd()->getTitle()->updateControlsVisibility();
 		onClose();
 	}
 }
diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp
index ccf5631df..6fcb66336 100644
--- a/Telegram/SourceFiles/historywidget.cpp
+++ b/Telegram/SourceFiles/historywidget.cpp
@@ -3065,9 +3065,9 @@ HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent)
 	connect(&_sendActionStopTimer, SIGNAL(timeout()), this, SLOT(onCancelSendAction()));
 	connect(&_previewTimer, SIGNAL(timeout()), this, SLOT(onPreviewTimeout()));
 	if (audioCapture()) {
-		connect(audioCapture(), SIGNAL(onError()), this, SLOT(onRecordError()));
-		connect(audioCapture(), SIGNAL(onUpdate(quint16,qint32)), this, SLOT(onRecordUpdate(quint16,qint32)));
-		connect(audioCapture(), SIGNAL(onDone(QByteArray,VoiceWaveform,qint32)), this, SLOT(onRecordDone(QByteArray,VoiceWaveform,qint32)));
+		connect(audioCapture(), SIGNAL(error()), this, SLOT(onRecordError()));
+		connect(audioCapture(), SIGNAL(updated(quint16,qint32)), this, SLOT(onRecordUpdate(quint16,qint32)));
+		connect(audioCapture(), SIGNAL(done(QByteArray,VoiceWaveform,qint32)), this, SLOT(onRecordDone(QByteArray,VoiceWaveform,qint32)));
 	}
 
 	_updateHistoryItems.setSingleShot(true);
@@ -5719,7 +5719,7 @@ void HistoryWidget::mouseReleaseEvent(QMouseEvent *e) {
 }
 
 void HistoryWidget::stopRecording(bool send) {
-	audioCapture()->stop(send);
+	emit audioCapture()->stop(send);
 
 	a_recordingLevel = anim::ivalue(0, 0);
 	_a_recording.stop();
@@ -7411,7 +7411,7 @@ void HistoryWidget::mousePressEvent(QMouseEvent *e) {
 	if (_replyForwardPressed && !_fieldBarCancel.isHidden()) {
 		updateField();
 	} else if (_inRecord && cHasAudioCapture()) {
-		audioCapture()->start();
+		emit audioCapture()->start();
 
 		_recording = _inField = true;
 		updateControlsVisibility();
diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp
index ff5765320..6e2fe177a 100644
--- a/Telegram/SourceFiles/mainwidget.cpp
+++ b/Telegram/SourceFiles/mainwidget.cpp
@@ -77,11 +77,6 @@ MainWidget::MainWidget(MainWindow *window) : TWidget(window)
 , _api(new ApiWrap(this)) {
 	setGeometry(QRect(0, st::titleHeight, App::wnd()->width(), App::wnd()->height() - st::titleHeight));
 
-	if (!_mediaPlayer) {
-		_mediaPlayer = new Media::Player::Widget(this);
-		App::wnd()->getTitle()->playerButton()->installEventFilter(_mediaPlayer);
-	}
-
 	MTP::setGlobalDoneHandler(rpcDone(&MainWidget::updateReceived));
 	_ptsWaiter.setRequesting(true);
 	updateScrollColors();
@@ -128,7 +123,7 @@ MainWidget::MainWidget(MainWindow *window) : TWidget(window)
 	} else {
 		_history->show();
 	}
-	App::wnd()->getTitle()->updateBackButton();
+	App::wnd()->getTitle()->updateControlsVisibility();
 	_topBar->hide();
 
 	_player->hidePlayer();
@@ -574,7 +569,7 @@ void MainWidget::noHider(HistoryHider *destroyed) {
 					_history->showAnimated(Window::SlideDirection::FromRight, animationParams);
 				}
 			}
-			App::wnd()->getTitle()->updateBackButton();
+			App::wnd()->getTitle()->updateControlsVisibility();
 		} else {
 			if (_forwardConfirm) {
 				_forwardConfirm->deleteLater();
@@ -611,7 +606,7 @@ void MainWidget::hiderLayer(HistoryHider *h) {
 			resizeEvent(0);
 			_dialogs->showAnimated(Window::SlideDirection::FromLeft, animationParams);
 		}
-		App::wnd()->getTitle()->updateBackButton();
+		App::wnd()->getTitle()->updateControlsVisibility();
 	} else {
 		_hider->show();
 		resizeEvent(0);
@@ -1549,7 +1544,6 @@ void MainWidget::ui_autoplayMediaInlineAsync(qint32 channelId, qint32 msgId) {
 
 void MainWidget::audioPlayProgress(const AudioMsgId &audioId) {
 	if (audioId.type() == AudioMsgId::Type::Video) {
-		audioPlayer()->videoSoundProgress(audioId);
 		return;
 	}
 
@@ -1559,8 +1553,8 @@ void MainWidget::audioPlayProgress(const AudioMsgId &audioId) {
 		playbackState.state = AudioPlayerStopped;
 		audioPlayer()->clearStoppedAtStart(audioId);
 
-		DocumentData *audio = audioId.audio();
-		QString filepath = audio->filepath(DocumentData::FilePathResolveSaveFromData);
+		auto document = audioId.audio();
+		auto filepath = document->filepath(DocumentData::FilePathResolveSaveFromData);
 		if (!filepath.isEmpty()) {
 			if (documentIsValidMediaFile(filepath)) {
 				psOpenFile(filepath);
@@ -1583,14 +1577,12 @@ void MainWidget::audioPlayProgress(const AudioMsgId &audioId) {
 		}
 	}
 
-	if (audioId.type() != AudioMsgId::Type::Video) {
-		if (auto item = App::histItemById(audioId.contextId())) {
-			Ui::repaintHistoryItem(item);
-		}
-		if (auto items = InlineBots::Layout::documentItems()) {
-			for (auto item : items->value(audioId.audio())) {
-				Ui::repaintInlineItem(item);
-			}
+	if (auto item = App::histItemById(audioId.contextId())) {
+		Ui::repaintHistoryItem(item);
+	}
+	if (auto items = InlineBots::Layout::documentItems()) {
+		for (auto item : items->value(audioId.audio())) {
+			Ui::repaintInlineItem(item);
 		}
 	}
 }
@@ -2142,7 +2134,7 @@ void MainWidget::ui_showPeerHistory(quint64 peerId, qint32 showAtMsgId, Ui::Show
 		_dialogs->update();
 	}
 	topBar()->showAll();
-	App::wnd()->getTitle()->updateBackButton();
+	App::wnd()->getTitle()->updateControlsVisibility();
 }
 
 PeerData *MainWidget::ui_getPeerForMouseAction() {
@@ -2264,7 +2256,7 @@ void MainWidget::showMediaOverview(PeerData *peer, MediaOverviewType type, bool
 
 	orderWidgets();
 
-	App::wnd()->getTitle()->updateBackButton();
+	App::wnd()->getTitle()->updateControlsVisibility();
 }
 
 void MainWidget::showWideSection(const Window::SectionMemento &memento) {
@@ -2363,7 +2355,7 @@ void MainWidget::showWideSectionAnimated(const Window::SectionMemento *memento,
 
 	orderWidgets();
 
-	App::wnd()->getTitle()->updateBackButton();
+	App::wnd()->getTitle()->updateControlsVisibility();
 }
 
 bool MainWidget::stackIsEmpty() const {
@@ -3285,8 +3277,9 @@ void MainWidget::mtpPing() {
 void MainWidget::start(const MTPUser &user) {
 	int32 uid = user.c_user().vid.v;
 	if (MTP::authedId() != uid) {
-		MTP::authed(uid);
+		MTP::setAuthedId(uid);
 		Local::writeMtpData();
+		App::wnd()->getTitle()->updateControlsVisibility();
 	}
 
 	Local::readSavedPeers();
diff --git a/Telegram/SourceFiles/mainwidget.h b/Telegram/SourceFiles/mainwidget.h
index 657f944a4..c91029e51 100644
--- a/Telegram/SourceFiles/mainwidget.h
+++ b/Telegram/SourceFiles/mainwidget.h
@@ -137,7 +137,6 @@ class MainWidget : public TWidget, public RPCSender, private base::Subscriber {
 	Q_OBJECT
 
 public:
-
 	MainWidget(MainWindow *window);
 
 	void paintEvent(QPaintEvent *e) override;
@@ -413,7 +412,6 @@ public:
 	~MainWidget();
 
 signals:
-
 	void peerUpdated(PeerData *peer);
 	void peerNameChanged(PeerData *peer, const PeerData::Names &oldNames, const PeerData::NameFirstChars &oldChars);
 	void peerPhotoChanged(PeerData *peer);
@@ -423,7 +421,6 @@ signals:
 	void savedGifsUpdated();
 
 public slots:
-
 	void webPagesUpdate();
 
 	void audioPlayProgress(const AudioMsgId &audioId);
@@ -480,8 +477,6 @@ public slots:
 	void ui_showPeerHistoryAsync(quint64 peerId, qint32 showAtMsgId, Ui::ShowWay way);
 	void ui_autoplayMediaInlineAsync(qint32 channelId, qint32 msgId);
 
-private slots:
-
 	void onDeletePhotoSure();
 
 private:
diff --git a/Telegram/SourceFiles/mainwindow.cpp b/Telegram/SourceFiles/mainwindow.cpp
index cc3abdd4a..14fbdb2d6 100644
--- a/Telegram/SourceFiles/mainwindow.cpp
+++ b/Telegram/SourceFiles/mainwindow.cpp
@@ -515,7 +515,7 @@ void MainWindow::clearWidgets() {
 		hideMediaview();
 		_mediaView->rpcClear();
 	}
-	title->updateBackButton();
+	title->updateControlsVisibility();
 	updateGlobalMenu();
 }
 
@@ -548,7 +548,7 @@ void MainWindow::clearPasscode() {
 		main->animShow(bg, true);
 	}
 	notifyUpdateAll();
-	title->updateBackButton();
+	title->updateControlsVisibility();
 	updateGlobalMenu();
 }
 
@@ -574,7 +574,7 @@ void MainWindow::setupPasscode(bool anim) {
 	}
 	_shouldLockAt = 0;
 	notifyUpdateAll();
-	title->updateBackButton();
+	title->updateControlsVisibility();
 	updateGlobalMenu();
 }
 
@@ -620,7 +620,7 @@ void MainWindow::setupIntro(bool anim) {
 
 	fixOrder();
 
-	updateTitleStatus();
+	updateConnectingStatus();
 
 	_delayedServiceMsgs.clear();
 	if (_serviceHistoryRequest) {
@@ -673,11 +673,11 @@ void MainWindow::setupMain(bool anim, const MTPUser *self) {
 	} else {
 		MTP::send(MTPusers_GetUsers(MTP_vector<MTPInputUser>(1, MTP_inputUserSelf())), main->rpcDone(&MainWidget::startFull));
 	}
-	title->resizeEvent(0);
+	title->updateControlsVisibility();
 
 	fixOrder();
 
-	updateTitleStatus();
+	updateConnectingStatus();
 }
 
 void MainWindow::updateUnreadCounter() {
@@ -713,12 +713,12 @@ void MainWindow::ui_hideSettingsAndLayer(ShowLayerOptions options) {
 
 void MainWindow::mtpStateChanged(int32 dc, int32 state) {
 	if (dc == MTP::maindc()) {
-		updateTitleStatus();
+		updateConnectingStatus();
 		Global::RefConnectionTypeChanged().notify();
 	}
 }
 
-void MainWindow::updateTitleStatus() {
+void MainWindow::updateConnectingStatus() {
 	int32 state = MTP::dcstate();
 	if (state == MTP::ConnectingState || state == MTP::DisconnectedState || (state < 0 && state > -600)) {
 		if (main || getms() > 5000 || _connecting) {
@@ -726,7 +726,7 @@ void MainWindow::updateTitleStatus() {
 		}
 	} else if (state < 0) {
 		showConnecting(lng_reconnecting(lt_count, ((-state) / 1000) + 1), lang(lng_reconnecting_try_now));
-		QTimer::singleShot((-state) % 1000, this, SLOT(updateTitleStatus()));
+		QTimer::singleShot((-state) % 1000, this, SLOT(updateConnectingStatus()));
 	} else {
 		hideConnecting();
 	}
@@ -853,28 +853,21 @@ void MainWindow::showConnecting(const QString &text, const QString &reconnect) {
 	if (_connecting) {
 		_connecting->set(text, reconnect);
 	} else {
-		_connecting = new ConnectingWidget(this, text, reconnect);
+		_connecting.create(this, text, reconnect);
 		_connecting->show();
 		resizeEvent(0);
 		fixOrder();
 	}
-	if (settings) settings->update();
-}
-
-bool MainWindow::connectingVisible() const {
-	return _connecting && !_connecting->isHidden();
 }
 
 void MainWindow::hideConnecting() {
 	if (_connecting) {
-		_connecting->deleteLater();
-		_connecting = 0;
+		_connecting.destroyDelayed();
 	}
-	if (settings) settings->update();
 }
 
 bool MainWindow::doWeReadServerHistory() const {
-	return isActive(false) && main && (!settings || !settings->isVisible()) && main->doWeReadServerHistory();
+	return isActive(false) && main && !Ui::isLayerShown() && main->doWeReadServerHistory();
 }
 
 void MainWindow::checkHistoryActivation() {
diff --git a/Telegram/SourceFiles/mainwindow.h b/Telegram/SourceFiles/mainwindow.h
index edd20a794..bc28dada0 100644
--- a/Telegram/SourceFiles/mainwindow.h
+++ b/Telegram/SourceFiles/mainwindow.h
@@ -175,10 +175,6 @@ public:
 	MainWidget *mainWidget();
 	PasscodeWidget *passcodeWidget();
 
-	void showConnecting(const QString &text, const QString &reconnect = QString());
-	void hideConnecting();
-	bool connectingVisible() const;
-
 	void showPhoto(const PhotoOpenClickHandler *lnk, HistoryItem *item = 0);
 	void showPhoto(PhotoData *photo, HistoryItem *item);
 	void showPhoto(PhotoData *photo, PeerData *item);
@@ -204,7 +200,7 @@ public:
 	TempDirState localStorageState();
 	void tempDirDelete(int task);
 
-    void notifySettingGot();
+	void notifySettingGot();
 	void notifySchedule(History *history, HistoryItem *item);
 	void notifyClear(History *history = 0);
 	void notifyClearFast();
@@ -215,7 +211,7 @@ public:
 	void notifyUpdateAll();
 	void notifyActivateAll();
 
-    QImage iconLarge() const;
+	QImage iconLarge() const;
 
 	void sendPaths();
 
@@ -257,7 +253,7 @@ public slots:
 	void showSettings();
 	void layerHidden();
 	void setInnerFocus();
-	void updateTitleStatus();
+	void updateConnectingStatus();
 
 	void quitFromTray();
 	void showFromTray(QSystemTrayIcon::ActivationReason reason = QSystemTrayIcon::Unknown);
@@ -294,11 +290,13 @@ signals:
 
 	void imageLoaded();
 
-private slots:
+	private slots:
 	void onStateChanged(Qt::WindowState state);
 	void onSettingsDestroyed(QObject *was);
 
 private:
+	void showConnecting(const QString &text, const QString &reconnect = QString());
+	void hideConnecting();
 
 	QPixmap grabInner();
 
@@ -322,7 +320,7 @@ private:
 	QTimer _isActiveTimer;
 	bool _isActive = false;
 
-	ConnectingWidget *_connecting = nullptr;
+	ChildWidget<ConnectingWidget> _connecting = { nullptr };
 
 	Local::ClearManager *_clearManager = nullptr;
 
diff --git a/Telegram/SourceFiles/media/media_audio.cpp b/Telegram/SourceFiles/media/media_audio.cpp
index 8a3854e08..16a7362be 100644
--- a/Telegram/SourceFiles/media/media_audio.cpp
+++ b/Telegram/SourceFiles/media/media_audio.cpp
@@ -308,6 +308,7 @@ _loader(new AudioPlayerLoaders(&_loaderThread)) {
 	connect(_fader, SIGNAL(audioStopped(const AudioMsgId&)), this, SLOT(onStopped(const AudioMsgId&)));
 	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();
 }
@@ -344,6 +345,13 @@ AudioPlayer::~AudioPlayer() {
 	_loaderThread.wait();
 }
 
+void AudioPlayer::onUpdated(const AudioMsgId &audio) {
+	if (audio.type() == AudioMsgId::Type::Video) {
+		videoSoundProgress(audio);
+	}
+	notify(AudioMsgId(audio));
+}
+
 void AudioPlayer::onError(const AudioMsgId &audio) {
 	emit stoppedOnError(audio);
 	if (audio.type() == AudioMsgId::Type::Voice) {
@@ -906,24 +914,16 @@ bool audioCheckError() {
 } // namespace internal
 
 AudioCapture::AudioCapture() : _capture(new AudioCaptureInner(&_captureThread)) {
-	connect(this, SIGNAL(captureOnStart()), _capture, SLOT(onStart()));
-	connect(this, SIGNAL(captureOnStop(bool)), _capture, SLOT(onStop(bool)));
-	connect(_capture, SIGNAL(done(QByteArray,VoiceWaveform,qint32)), this, SIGNAL(onDone(QByteArray,VoiceWaveform,qint32)));
-	connect(_capture, SIGNAL(update(quint16,qint32)), this, SIGNAL(onUpdate(quint16,qint32)));
-	connect(_capture, SIGNAL(error()), this, SIGNAL(onError()));
+	connect(this, SIGNAL(start()), _capture, SLOT(onStart()));
+	connect(this, SIGNAL(stop(bool)), _capture, SLOT(onStop(bool)));
+	connect(_capture, SIGNAL(done(QByteArray,VoiceWaveform,qint32)), this, SIGNAL(done(QByteArray,VoiceWaveform,qint32)));
+	connect(_capture, SIGNAL(updated(quint16,qint32)), this, SIGNAL(updated(quint16,qint32)));
+	connect(_capture, SIGNAL(error()), this, SIGNAL(error()));
 	connect(&_captureThread, SIGNAL(started()), _capture, SLOT(onInit()));
 	connect(&_captureThread, SIGNAL(finished()), _capture, SLOT(deleteLater()));
 	_captureThread.start();
 }
 
-void AudioCapture::start() {
-	emit captureOnStart();
-}
-
-void AudioCapture::stop(bool needResult) {
-	emit captureOnStop(needResult);
-}
-
 bool AudioCapture::check() {
 	if (auto defaultDevice = alcGetString(0, ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER)) {
 		if (auto device = alcCaptureOpenDevice(defaultDevice, AudioVoiceMsgFrequency, AL_FORMAT_MONO16, AudioVoiceMsgFrequency / 5)) {
@@ -1664,7 +1664,7 @@ void AudioCaptureInner::onTimeout() {
 		}
 		qint32 samplesFull = d->fullSamples + _captured.size() / sizeof(short), samplesSinceUpdate = samplesFull - d->lastUpdate;
 		if (samplesSinceUpdate > AudioVoiceMsgUpdateView * AudioVoiceMsgFrequency / 1000) {
-			emit update(d->levelMax, samplesFull);
+			emit updated(d->levelMax, samplesFull);
 			d->lastUpdate = samplesFull;
 			d->levelMax = 0;
 		}
diff --git a/Telegram/SourceFiles/media/media_audio.h b/Telegram/SourceFiles/media/media_audio.h
index 87b8cfd08..a0e767b91 100644
--- a/Telegram/SourceFiles/media/media_audio.h
+++ b/Telegram/SourceFiles/media/media_audio.h
@@ -55,7 +55,7 @@ struct AudioPlaybackState {
 	int32 frequency = 0;
 };
 
-class AudioPlayer : public QObject {
+class AudioPlayer : public QObject, public base::Observable<AudioMsgId> {
 	Q_OBJECT
 
 public:
@@ -70,7 +70,6 @@ public:
 	void initFromVideo(uint64 videoPlayId, std_::unique_ptr<VideoSoundData> &&data, int64 position);
 	void feedFromVideo(VideoSoundPart &&part);
 	int64 getVideoCorrectedTime(uint64 playId, int64 frameMs, uint64 systemMs);
-	void videoSoundProgress(const AudioMsgId &audio);
 	AudioPlaybackState currentVideoState(uint64 videoPlayId);
 	void stopFromVideo(uint64 videoPlayId);
 	void pauseFromVideo(uint64 videoPlayId);
@@ -86,10 +85,12 @@ public:
 
 	~AudioPlayer();
 
-public slots:
+private slots:
 	void onError(const AudioMsgId &audio);
 	void onStopped(const AudioMsgId &audio);
 
+	void onUpdated(const AudioMsgId &audio);
+
 signals:
 	void updated(const AudioMsgId &audio);
 	void stoppedOnError(const AudioMsgId &audio);
@@ -110,6 +111,8 @@ private:
 	bool updateCurrentStarted(AudioMsgId::Type type, int32 pos = -1);
 	bool checkCurrentALError(AudioMsgId::Type type);
 
+	void videoSoundProgress(const AudioMsgId &audio);
+
 	struct AudioMsg {
 		void clear();
 
@@ -185,27 +188,21 @@ class AudioCapture : public QObject {
 	Q_OBJECT
 
 public:
-
 	AudioCapture();
 
-	void start();
-	void stop(bool needResult);
-
 	bool check();
 
 	~AudioCapture();
 
 signals:
+	void start();
+	void stop(bool needResult);
 
-	void captureOnStart();
-	void captureOnStop(bool needResult);
-
-	void onDone(QByteArray data, VoiceWaveform waveform, qint32 samples);
-	void onUpdate(quint16 level, qint32 samples);
-	void onError();
+	void done(QByteArray data, VoiceWaveform waveform, qint32 samples);
+	void updated(quint16 level, qint32 samples);
+	void error();
 
 private:
-
 	friend class AudioCaptureInner;
 
 	QThread _captureThread;
@@ -270,18 +267,15 @@ class AudioCaptureInner : public QObject {
 	Q_OBJECT
 
 public:
-
 	AudioCaptureInner(QThread *thread);
 	~AudioCaptureInner();
 
 signals:
-
 	void error();
-	void update(quint16 level, qint32 samples);
+	void updated(quint16 level, qint32 samples);
 	void done(QByteArray data, VoiceWaveform waveform, qint32 samples);
 
-	public slots:
-
+public slots:
 	void onInit();
 	void onStart();
 	void onStop(bool needResult);
@@ -289,7 +283,6 @@ signals:
 	void onTimeout();
 
 private:
-
 	void processFrame(int32 offset, int32 framesize);
 
 	void writeFrame(AVFrame *frame);
diff --git a/Telegram/SourceFiles/mtproto/facade.cpp b/Telegram/SourceFiles/mtproto/facade.cpp
index a5293f353..43498d474 100644
--- a/Telegram/SourceFiles/mtproto/facade.cpp
+++ b/Telegram/SourceFiles/mtproto/facade.cpp
@@ -851,7 +851,7 @@ void finish() {
 	_started = false;
 }
 
-void authed(int32 uid) {
+void setAuthedId(int32 uid) {
 	internal::authed(uid);
 }
 
diff --git a/Telegram/SourceFiles/mtproto/facade.h b/Telegram/SourceFiles/mtproto/facade.h
index 6634fb812..15741c44c 100644
--- a/Telegram/SourceFiles/mtproto/facade.h
+++ b/Telegram/SourceFiles/mtproto/facade.h
@@ -180,7 +180,7 @@ int32 state(mtpRequestId req); // < 0 means waiting for such count of ms
 
 void finish();
 
-void authed(int32 uid);
+void setAuthedId(int32 uid);
 int32 authedId();
 void logoutKeys(RPCDoneHandlerPtr onDone, RPCFailHandlerPtr onFail);
 
diff --git a/Telegram/SourceFiles/settings/settings_scale_widget.cpp b/Telegram/SourceFiles/settings/settings_scale_widget.cpp
index 7660e5050..4f4f714d1 100644
--- a/Telegram/SourceFiles/settings/settings_scale_widget.cpp
+++ b/Telegram/SourceFiles/settings/settings_scale_widget.cpp
@@ -217,7 +217,7 @@ void ScaleWidget::setScale(DBIScale newScale) {
 
 	cSetConfigScale(newScale);
 	Local::writeSettings();
-	App::wnd()->getTitle()->showUpdateBtn();
+	App::wnd()->getTitle()->updateControlsVisibility();
 	if (newScale == dbisAuto && !_auto->checked()) {
 		_auto->setChecked(true);
 	} else if (newScale != dbisAuto && _auto->checked()) {
diff --git a/Telegram/SourceFiles/sysbuttons.cpp b/Telegram/SourceFiles/sysbuttons.cpp
index 0614cf322..7b429cb86 100644
--- a/Telegram/SourceFiles/sysbuttons.cpp
+++ b/Telegram/SourceFiles/sysbuttons.cpp
@@ -35,7 +35,6 @@ SysBtn::SysBtn(QWidget *parent, const style::sysButton &st, const QString &text)
 	int32 w = _st.size.width() + (_text.isEmpty() ? 0 : ((_st.size.width() - _st.img.pxWidth()) / 2 + st::titleTextButton.font->width(_text)));
 	resize(w, _st.size.height());
 	setCursor(style::cur_default);
-	connect(this, SIGNAL(stateChanged(int, ButtonStateChangeSource)), this, SLOT(onStateChange(int, ButtonStateChangeSource)));
 }
 
 void SysBtn::setText(const QString &text) {
@@ -49,7 +48,7 @@ void SysBtn::setOverLevel(float64 level) {
 	update();
 }
 
-void SysBtn::onStateChange(int oldState, ButtonStateChangeSource source) {
+void SysBtn::onStateChanged(int oldState, ButtonStateChangeSource source) {
 	a_color.start((_state & StateOver ? _st.overColor : _st.color)->c);
 
 	if (source == ButtonByUser || source == ButtonByPress) {
@@ -109,60 +108,48 @@ void SysBtn::step_color(float64 ms, bool timer) {
 	if (timer) update();
 }
 
-MinimizeBtn::MinimizeBtn(QWidget *parent, MainWindow *window) : SysBtn(parent, st::sysMin), wnd(window) {
-	connect(this, SIGNAL(clicked()), this, SLOT(onClick()));
+MinimizeBtn::MinimizeBtn(QWidget *parent) : SysBtn(parent, st::sysMin) {
+	setClickedCallback([this]() {
+		window()->setWindowState(Qt::WindowMinimized);
+	});
 }
 
-void MinimizeBtn::onClick() {
-	wnd->setWindowState(Qt::WindowMinimized);
+MaximizeBtn::MaximizeBtn(QWidget *parent) : SysBtn(parent, st::sysMax) {
+	setClickedCallback([this]() {
+		window()->setWindowState(Qt::WindowMaximized);
+	});
 }
 
-MaximizeBtn::MaximizeBtn(QWidget *parent, MainWindow *window) : SysBtn(parent, st::sysMax), wnd(window) {
-	connect(this, SIGNAL(clicked()), this, SLOT(onClick()));
+RestoreBtn::RestoreBtn(QWidget *parent) : SysBtn(parent, st::sysRes) {
+	setClickedCallback([this]() {
+		window()->setWindowState(Qt::WindowNoState);
+	});
 }
 
-void MaximizeBtn::onClick() {
-	wnd->setWindowState(Qt::WindowMaximized);
+CloseBtn::CloseBtn(QWidget *parent) : SysBtn(parent, st::sysCls) {
+	setClickedCallback([this]() {
+		window()->close();
+	});
 }
 
-RestoreBtn::RestoreBtn(QWidget *parent, MainWindow *window) : SysBtn(parent, st::sysRes), wnd(window) {
-	connect(this, SIGNAL(clicked()), this, SLOT(onClick()));
-}
-
-void RestoreBtn::onClick() {
-	wnd->setWindowState(Qt::WindowNoState);
-}
-
-CloseBtn::CloseBtn(QWidget *parent, MainWindow *window) : SysBtn(parent, st::sysCls), wnd(window) {
-	connect(this, SIGNAL(clicked()), this, SLOT(onClick()));
-}
-
-void CloseBtn::onClick() {
-	wnd->close();
-}
-
-UpdateBtn::UpdateBtn(QWidget *parent, MainWindow *window, const QString &text) : SysBtn(parent, st::sysUpd, text), wnd(window) {
-	connect(this, SIGNAL(clicked()), this, SLOT(onClick()));
-}
-
-void UpdateBtn::onClick() {
+UpdateBtn::UpdateBtn(QWidget *parent) : SysBtn(parent, st::sysUpd, lang(lng_menu_update)) {
+	setClickedCallback([]() {
 #ifndef TDESKTOP_DISABLE_AUTOUPDATE
-	checkReadyUpdate();
-	if (Sandbox::updatingState() == Application::UpdatingReady) {
-		cSetRestartingUpdate(true);
-	} else
+		checkReadyUpdate();
+		if (Sandbox::updatingState() == Application::UpdatingReady) {
+			cSetRestartingUpdate(true);
+		} else
 #endif
-	{
-		cSetRestarting(true);
-		cSetRestartingToSettings(false);
-	}
-	App::quit();
+		{
+			cSetRestarting(true);
+			cSetRestartingToSettings(false);
+		}
+		App::quit();
+	});
 }
 
-LockBtn::LockBtn(QWidget *parent, MainWindow *window) : SysBtn(parent, st::sysLock), wnd(window) {
-	connect(this, SIGNAL(clicked()), this, SLOT(onClick()));
-}
-
-void LockBtn::onClick() {
-	Shortcuts::launch(qsl("lock_telegram"));
+LockBtn::LockBtn(QWidget *parent) : SysBtn(parent, st::sysLock) {
+	setClickedCallback([] {
+		Shortcuts::launch(qsl("lock_telegram"));
+	});
 }
diff --git a/Telegram/SourceFiles/sysbuttons.h b/Telegram/SourceFiles/sysbuttons.h
index 88ff7b0a1..537c17b18 100644
--- a/Telegram/SourceFiles/sysbuttons.h
+++ b/Telegram/SourceFiles/sysbuttons.h
@@ -23,13 +23,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
 #include "ui/animation.h"
 #include "ui/button.h"
 
-class MainWindow;
-
 class SysBtn : public Button {
-	Q_OBJECT
-
 public:
-
 	SysBtn(QWidget *parent, const style::sysButton &st, const QString &text = QString());
 
 	void setText(const QString &text);
@@ -42,11 +37,8 @@ public:
 
 	void step_color(float64 ms, bool timer);
 
-public slots:
-
-	void onStateChange(int oldState, ButtonStateChangeSource source);
-
 protected:
+	void onStateChanged(int oldState, ButtonStateChangeSource source) override;
 
 	style::sysButton _st;
 	anim::cvalue a_color;
@@ -58,97 +50,37 @@ protected:
 };
 
 class MinimizeBtn : public SysBtn {
-	Q_OBJECT
-
 public:
+	MinimizeBtn(QWidget *parent);
 
-	MinimizeBtn(QWidget *parent, MainWindow *window);
-
-public slots:
-
-	void onClick();
-
-private:
-
-	MainWindow *wnd;
 };
 
 class MaximizeBtn : public SysBtn {
-	Q_OBJECT
-
 public:
+	MaximizeBtn(QWidget *parent);
 
-	MaximizeBtn(QWidget *parent, MainWindow *window);
-
-public slots:
-
-	void onClick();
-
-private:
-
-	MainWindow *wnd;
 };
 
 class RestoreBtn : public SysBtn {
-	Q_OBJECT
-
 public:
+	RestoreBtn(QWidget *parent);
 
-	RestoreBtn(QWidget *parent, MainWindow *window);
-
-public slots:
-
-	void onClick();
-
-private:
-
-	MainWindow *wnd;
 };
 
 class CloseBtn : public SysBtn {
-	Q_OBJECT
-
 public:
+	CloseBtn(QWidget *parent);
 
-	CloseBtn(QWidget *parent, MainWindow *window);
-
-public slots:
-
-	void onClick();
-
-private:
-
-	MainWindow *wnd;
 };
 
 class UpdateBtn : public SysBtn {
-	Q_OBJECT
-
 public:
+	UpdateBtn(QWidget *parent);
 
-	UpdateBtn(QWidget *parent, MainWindow *window, const QString &text = QString());
-
-public slots:
-
-	void onClick();
-
-private:
-
-	MainWindow *wnd;
 };
 
 class LockBtn : public SysBtn {
-	Q_OBJECT
-
 public:
+	LockBtn(QWidget *parent);
 
-	LockBtn(QWidget *parent, MainWindow *window);
-
-public slots:
-
-	void onClick();
-
-private:
-
-	MainWindow *wnd;
 };
diff --git a/Telegram/SourceFiles/title.cpp b/Telegram/SourceFiles/title.cpp
index 05adda84b..3c370c011 100644
--- a/Telegram/SourceFiles/title.cpp
+++ b/Telegram/SourceFiles/title.cpp
@@ -27,71 +27,86 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
 #include "application.h"
 #include "boxes/contactsbox.h"
 #include "boxes/aboutbox.h"
+#include "media/media_audio.h"
 #include "media/player/media_player_button.h"
 
-TitleHider::TitleHider(QWidget *parent) : QWidget(parent), _level(0) {
+class TitleWidget::Hider : public TWidget {
+public:
+	Hider(QWidget *parent);
+
+	using ClickedCallback = base::lambda_unique<void()>;
+	void setClickedCallback(ClickedCallback &&callback) {
+		_callback = std_::move(callback);
+	}
+	void setLevel(float64 level);
+
+protected:
+	void paintEvent(QPaintEvent *e) override;
+	void mousePressEvent(QMouseEvent *e) override;
+
+private:
+	ClickedCallback _callback;
+	float64 _level = 0;
+
+};
+
+TitleWidget::Hider::Hider(QWidget *parent) : TWidget(parent) {
 }
 
-void TitleHider::paintEvent(QPaintEvent *e) {
+void TitleWidget::Hider::paintEvent(QPaintEvent *e) {
 	QPainter p(this);
 	p.setOpacity(_level * st::layerAlpha);
 	p.fillRect(App::main()->dlgsWidth(), 0, width() - App::main()->dlgsWidth(), height(), st::layerBg->b);
 }
 
-void TitleHider::mousePressEvent(QMouseEvent *e) {
-	if (e->button() == Qt::LeftButton) {
-		emit static_cast<TitleWidget*>(parentWidget())->hiderClicked();
+void TitleWidget::Hider::mousePressEvent(QMouseEvent *e) {
+	if (e->button() == Qt::LeftButton && _callback) {
+		_callback();
 	}
 }
 
-void TitleHider::setLevel(float64 level) {
+void TitleWidget::Hider::setLevel(float64 level) {
 	_level = level;
 	update();
 }
 
-TitleWidget::TitleWidget(MainWindow *window) : TWidget(window)
-, wnd(window)
-, hideLevel(0)
-, hider(0)
+TitleWidget::TitleWidget(QWidget *parent) : TWidget(parent)
 , _cancel(this, lang(lng_cancel), st::titleTextButton)
 , _settings(this, lang(lng_menu_settings), st::titleTextButton)
 , _contacts(this, lang(lng_menu_contacts), st::titleTextButton)
 , _about(this, lang(lng_menu_about), st::titleTextButton)
-, _player(this)
-, _lock(this, window)
-, _update(this, window, lang(lng_menu_update))
-, _minimize(this, window)
-, _maximize(this, window)
-, _restore(this, window)
-, _close(this, window)
+, _lock(this)
+, _update(this)
+, _minimize(this)
+, _maximize(this)
+, _restore(this)
+, _close(this)
 , _a_update(animation(this, &TitleWidget::step_update))
-, lastMaximized(!(window->windowState() & Qt::WindowMaximized)) {
-	setGeometry(0, 0, wnd->width(), st::titleHeight);
+, lastMaximized(!(parent->windowState() & Qt::WindowMaximized)) {
+	setGeometry(0, 0, parent->width(), st::titleHeight);
 	setAttribute(Qt::WA_OpaquePaintEvent);
-	_lock.hide();
-	_update.hide();
-    _cancel.hide();
-	if (
-#ifndef TDESKTOP_DISABLE_AUTOUPDATE
-		Sandbox::updatingState() == Application::UpdatingReady ||
-#endif
-		Global::LocalPasscode()
-	) {
-		showUpdateBtn();
-	}
+
 	onWindowStateChanged();
+	updateControlsVisibility();
 
 	connect(&_cancel, SIGNAL(clicked()), this, SIGNAL(hiderClicked()));
-	connect(&_settings, SIGNAL(clicked()), window, SLOT(showSettings()));
+	connect(&_settings, SIGNAL(clicked()), parent, SLOT(showSettings()));
 	connect(&_contacts, SIGNAL(clicked()), this, SLOT(onContacts()));
 	connect(&_about, SIGNAL(clicked()), this, SLOT(onAbout()));
-	connect(wnd->windowHandle(), SIGNAL(windowStateChanged(Qt::WindowState)), this, SLOT(onWindowStateChanged(Qt::WindowState)));
+	connect(parent->windowHandle(), SIGNAL(windowStateChanged(Qt::WindowState)), this, SLOT(onWindowStateChanged(Qt::WindowState)));
 
 #ifndef TDESKTOP_DISABLE_AUTOUPDATE
-	Sandbox::connect(SIGNAL(updateReady()), this, SLOT(showUpdateBtn()));
+	Sandbox::connect(SIGNAL(updateReady()), this, SLOT(updateControlsVisibility()));
 #endif
 
 	subscribe(Adaptive::Changed(), [this]() { updateAdaptiveLayout(); });
+	if (auto player = audioPlayer()) {
+		subscribe(player, [this](const AudioMsgId &audio) {
+			if (audio.type() == AudioMsgId::Type::Song) {
+				handleSongUpdate(audio);
+			}
+		});
+	}
 
     if (cPlatform() != dbipWindows) {
         _minimize.hide();
@@ -127,20 +142,31 @@ void TitleWidget::setHideLevel(float64 level) {
 	if (level != hideLevel) {
 		hideLevel = level;
 		if (hideLevel) {
-			if (!hider) {
-				hider = new TitleHider(this);
-				hider->move(0, 0);
-				hider->resize(size());
-				if (Adaptive::OneColumn()) {
-					hider->hide();
-				} else {
-					hider->show();
-				}
+			if (!_hider) {
+				_hider.create(this);
+				_hider->setGeometry(rect());
+				_hider->setClickedCallback([this]() { emit hiderClicked(); });
+				_hider->setVisible(!Adaptive::OneColumn());
 			}
-			hider->setLevel(hideLevel);
+			_hider->setLevel(hideLevel);
 		} else {
-			if (hider) hider->deleteLater();
-			hider = 0;
+			if (_hider) {
+				_hider.destroyDelayed();
+			}
+		}
+	}
+}
+
+void TitleWidget::handleSongUpdate(const AudioMsgId &audioId) {
+	t_assert(audioId.type() == AudioMsgId::Type::Song);
+
+	AudioMsgId playing;
+	auto playbackState = audioPlayer()->currentState(&playing, audioId.type());
+	if (playing == audioId) {
+		auto songIsPlaying = !(playbackState.state & AudioPlayerStoppedMask) && (playbackState.state != AudioPlayerFinishing);
+		if (songIsPlaying && !_player) {
+			_player.create(this);
+			updateControlsVisibility();
 		}
 	}
 }
@@ -157,12 +183,7 @@ void TitleWidget::onAbout() {
 	Ui::showLayer(new AboutBox());
 }
 
-TitleWidget::~TitleWidget() {
-	delete hider;
-	hider = 0;
-}
-
-void TitleWidget::resizeEvent(QResizeEvent *e) {
+void TitleWidget::updateControlsPosition() {
 	QPoint p(width() - ((cPlatform() == dbipWindows && lastMaximized) ? 0 : st::sysBtnDelta), 0);
 
 	if (!_update.isHidden()) {
@@ -177,16 +198,16 @@ void TitleWidget::resizeEvent(QResizeEvent *e) {
 	}
 	_cancel.move(p.x() - _cancel.width(), 0);
 
-    if (cPlatform() == dbipWindows) {
-        p.setX(p.x() - _close.width());
-        _close.move(p);
+	if (cPlatform() == dbipWindows) {
+		p.setX(p.x() - _close.width());
+		_close.move(p);
 
-        p.setX(p.x() - _maximize.width());
-        _restore.move(p); _maximize.move(p);
+		p.setX(p.x() - _maximize.width());
+		_restore.move(p); _maximize.move(p);
 
-        p.setX(p.x() - _minimize.width());
-        _minimize.move(p);
-    }
+		p.setX(p.x() - _minimize.width());
+		_minimize.move(p);
+	}
 	if (_update.isHidden() && !_lock.isHidden()) {
 		p.setX(p.x() - _lock.width());
 		_lock.move(p);
@@ -197,61 +218,108 @@ void TitleWidget::resizeEvent(QResizeEvent *e) {
 	}
 
 	_settings.move(st::titleMenuOffset, 0);
-	if (MTP::authedId() && _cancel.isHidden() && !App::passcoded()) {
-		if (_contacts.isHidden()) _contacts.show();
+	if (_contacts.isHidden()) {
+		_about.move(_settings.x() + _settings.width(), 0);
+	} else {
 		_contacts.move(_settings.x() + _settings.width(), 0);
 		_about.move(_contacts.x() + _contacts.width(), 0);
-	} else {
-		if (!_contacts.isHidden()) _contacts.hide();
-		if (!MTP::authedId()) _about.move(_settings.x() + _settings.width(), 0);
 	}
 
-	if (hider) hider->resize(size());
+	if (_hider) {
+		_hider->resize(size());
+	}
 }
 
-void TitleWidget::updateBackButton() {
-	if (App::passcoded()) {
-		if (!_cancel.isHidden()) _cancel.hide();
-		if (!_settings.isHidden()) _settings.hide();
-		if (!_contacts.isHidden()) _contacts.hide();
-		if (!_about.isHidden()) _about.hide();
-		_lock.setSysBtnStyle(st::sysUnlock);
-	} else {
-		_lock.setSysBtnStyle(st::sysLock);
-		if (Adaptive::OneColumn() && App::main() && App::main()->selectingPeer()) {
-			_cancel.show();
-			if (!_settings.isHidden()) _settings.hide();
-			if (!_contacts.isHidden()) _contacts.hide();
-			if (!_about.isHidden()) _about.hide();
-		} else {
-			if (!_cancel.isHidden()) _cancel.hide();
-			bool authed = (MTP::authedId() > 0);
-			if (Adaptive::OneColumn()) {
-				if (_settings.isHidden()) _settings.show();
-				if (authed && _contacts.isHidden()) _contacts.show();
-				if (_about.isHidden()) _about.show();
-			} else {
-				if (_settings.isHidden()) _settings.show();
-				if (authed && _contacts.isHidden()) _contacts.show();
-				if (_about.isHidden()) _about.show();
-			}
-		}
-	}
-	showUpdateBtn();
+void TitleWidget::resizeEvent(QResizeEvent *e) {
+	updateControlsPosition();
+}
+
+void TitleWidget::updateControlsVisibility() {
+	auto passcoded = App::passcoded();
+	auto authed = (App::main() != nullptr);
+	auto selecting = authed && App::main()->selectingPeer();
+	auto oneColumnSelecting = (Adaptive::OneColumn() && selecting && !passcoded);
+
+	_cancel.setVisible(oneColumnSelecting);
+
+	updateRestartButtonVisibility();
+	updateMenuButtonsVisibility();
+	updateSystemButtonsVisibility();
+
+	updateControlsPosition();
 	update();
 }
 
+void TitleWidget::updateRestartButtonVisibility() {
+#ifndef TDESKTOP_DISABLE_AUTOUPDATE
+	bool updateReady = (Sandbox::updatingState() == Application::UpdatingReady);
+#else
+	bool updateReady = false;
+#endif
+	auto scaleRestarting = cEvalScale(cConfigScale()) != cEvalScale(cRealScale());
+
+	auto updateVisible = _cancel.isHidden() && (updateReady || scaleRestarting);
+	if (updateVisible) {
+		_update.setText(lang(updateReady ? lng_menu_update : lng_menu_restart));
+		_update.show();
+		_a_update.start();
+	} else {
+		_update.hide();
+		_a_update.stop();
+	}
+}
+
+void TitleWidget::updateMenuButtonsVisibility() {
+	if (_cancel.isHidden()) {
+		if (App::passcoded()) {
+			_settings.hide();
+			_contacts.hide();
+			_about.hide();
+			_lock.setSysBtnStyle(st::sysUnlock);
+		} else {
+			_lock.setSysBtnStyle(st::sysLock);
+			_settings.show();
+			_contacts.setVisible(App::main() != nullptr);
+			_about.show();
+		}
+	} else {
+		_settings.hide();
+		_contacts.hide();
+		_about.hide();
+	}
+}
+
+void TitleWidget::updateSystemButtonsVisibility() {
+	if (_cancel.isHidden()) {
+		_lock.setVisible(Global::LocalPasscode());
+		if (_player) {
+			_player->show();
+		}
+	} else {
+		_lock.hide();
+		if (_player) {
+			_player->hide();
+		}
+	}
+	if (_update.isHidden() && _cancel.isHidden() && cPlatform() == dbipWindows) {
+		_minimize.show();
+		maximizedChanged(lastMaximized, true);
+		_close.show();
+	} else {
+		_minimize.hide();
+		_restore.hide();
+		_maximize.hide();
+		_close.hide();
+	}
+}
+
 void TitleWidget::updateAdaptiveLayout() {
-	updateBackButton();
+	updateControlsVisibility();
 	if (Adaptive::OneColumn()) {
 		updateCounter();
 	}
-	if (hider) {
-		if (Adaptive::OneColumn()) {
-			hider->hide();
-		} else {
-			hider->show();
-		}
+	if (_hider) {
+		_hider->setVisible(!Adaptive::OneColumn());
 	}
 }
 
@@ -282,20 +350,24 @@ void TitleWidget::updateCounter() {
 }
 
 void TitleWidget::mousePressEvent(QMouseEvent *e) {
-	if (wnd->psHandleTitle()) return;
-	if (e->buttons() & Qt::LeftButton) {
-		wnd->wStartDrag(e);
-		e->accept();
+	if (auto wnd = App::wnd()) {
+		if (wnd->psHandleTitle()) return;
+		if (e->buttons() & Qt::LeftButton) {
+			wnd->wStartDrag(e);
+			e->accept();
+		}
 	}
 }
 
 void TitleWidget::mouseDoubleClickEvent(QMouseEvent *e) {
-	if (wnd->psHandleTitle()) return;
-	Qt::WindowStates s(wnd->windowState());
-	if (s.testFlag(Qt::WindowMaximized)) {
-		wnd->setWindowState(s & ~Qt::WindowMaximized);
-	} else {
-		wnd->setWindowState(s | Qt::WindowMaximized);
+	if (auto wnd = App::wnd()) {
+		if (wnd->psHandleTitle()) return;
+		Qt::WindowStates s(wnd->windowState());
+		if (s.testFlag(Qt::WindowMaximized)) {
+			wnd->setWindowState(s & ~Qt::WindowMaximized);
+		} else {
+			wnd->setWindowState(s | Qt::WindowMaximized);
+		}
 	}
 }
 
@@ -304,49 +376,6 @@ void TitleWidget::onWindowStateChanged(Qt::WindowState state) {
 	maximizedChanged(state == Qt::WindowMaximized);
 }
 
-void TitleWidget::showUpdateBtn() {
-	if (Adaptive::OneColumn() && App::main() && App::main()->selectingPeer()) {
-		_cancel.show();
-		_lock.hide();
-		_update.hide();
-		_minimize.hide();
-		_restore.hide();
-		_maximize.hide();
-		_close.hide();
-		return;
-	}
-	if (Global::LocalPasscode()) {
-		_lock.show();
-	} else {
-		_lock.hide();
-	}
-#ifndef TDESKTOP_DISABLE_AUTOUPDATE
-	bool updateReady = (Sandbox::updatingState() == Application::UpdatingReady);
-#else
-	bool updateReady = false;
-#endif
-	if (updateReady || cEvalScale(cConfigScale()) != cEvalScale(cRealScale())) {
-		_update.setText(lang(updateReady ? lng_menu_update : lng_menu_restart));
-		_update.show();
-		resizeEvent(0);
-		_minimize.hide();
-		_restore.hide();
-		_maximize.hide();
-		_close.hide();
-		_a_update.start();
-	} else {
-		_update.hide();
-		if (cPlatform() == dbipWindows) {
-			_minimize.show();
-			maximizedChanged(lastMaximized, true);
-			_close.show();
-		}
-		_a_update.stop();
-	}
-	resizeEvent(0);
-	update();
-}
-
 void TitleWidget::maximizedChanged(bool maximized, bool force) {
 	if (lastMaximized == maximized && !force) return;
 
@@ -362,14 +391,14 @@ void TitleWidget::maximizedChanged(bool maximized, bool force) {
 	_maximize.setVisible(!maximized);
 	_restore.setVisible(maximized);
 
-	resizeEvent(0);
+	updateControlsPosition();
 }
 
 HitTestType TitleWidget::hitTest(const QPoint &p) {
 	if (App::wnd() && Ui::isLayerShown()) return HitTestNone;
 
 	int x(p.x()), y(p.y()), w(width()), h(height());
-	if (!Adaptive::OneColumn() && hider && x >= App::main()->dlgsWidth()) return HitTestNone;
+	if (!Adaptive::OneColumn() && _hider && x >= App::main()->dlgsWidth()) return HitTestNone;
 
 	if (x >= st::titleIconPos.x() && y >= st::titleIconPos.y() && x < st::titleIconPos.x() + st::titleIconImg.pxWidth() && y < st::titleIconPos.y() + st::titleIconImg.pxHeight()) {
 		return HitTestIcon;
diff --git a/Telegram/SourceFiles/title.h b/Telegram/SourceFiles/title.h
index cb98ca335..137daad83 100644
--- a/Telegram/SourceFiles/title.h
+++ b/Telegram/SourceFiles/title.h
@@ -29,31 +29,14 @@ namespace Player {
 class TitleButton;
 } // namespace Player
 } // namespace Media
-
-class TitleHider : public QWidget {
-public:
-
-	TitleHider(QWidget *parent);
-	void paintEvent(QPaintEvent *e);
-	void mousePressEvent(QMouseEvent *e);
-	void setLevel(float64 level);
-
-private:
-
-	float64 _level;
-
-};
+class AudioMsgId;
 
 class TitleWidget : public TWidget, private base::Subscriber {
 	Q_OBJECT
 
 public:
+	TitleWidget(QWidget *parent);
 
-	TitleWidget(MainWindow *parent);
-	void paintEvent(QPaintEvent *e);
-	void resizeEvent(QResizeEvent *e);
-
-	void updateBackButton();
 	void updateCounter();
 
 	void mousePressEvent(QMouseEvent *e);
@@ -71,28 +54,33 @@ public:
 
 	void step_update(float64 ms, bool timer);
 
-	~TitleWidget();
-
 public slots:
-
 	void onWindowStateChanged(Qt::WindowState state = Qt::WindowNoState);
-	void showUpdateBtn();
+	void updateControlsVisibility();
 	void onContacts();
 	void onAbout();
 
 signals:
-
 	void hiderClicked();
 
+protected:
+	void paintEvent(QPaintEvent *e) override;
+	void resizeEvent(QResizeEvent *e) override;
+
 private:
 	void updateAdaptiveLayout();
+	void updateRestartButtonVisibility();
+	void updateMenuButtonsVisibility();
+	void updateSystemButtonsVisibility();
+	void updateControlsPosition();
 
-	MainWindow *wnd;
+	void handleSongUpdate(const AudioMsgId &audioId);
 
 	style::color statusColor;
 
-	float64 hideLevel;
-	TitleHider *hider;
+	class Hider;
+	float64 hideLevel = 0;
+	ChildWidget<Hider> _hider = { nullptr };
 
 	float64 _lastUpdateMs;
 
diff --git a/Telegram/SourceFiles/ui/twidget.h b/Telegram/SourceFiles/ui/twidget.h
index b12c4da1b..31ae6d2f4 100644
--- a/Telegram/SourceFiles/ui/twidget.h
+++ b/Telegram/SourceFiles/ui/twidget.h
@@ -350,6 +350,12 @@ public:
 		return ptr();
 	}
 
+	// Use that instead "= new T(parent, ...)"
+	template <typename Parent, typename... Args>
+	void create(Parent &&parent, Args&&... args) {
+		delete _widget;
+		_widget = new T(std_::forward<Parent>(parent), std_::forward<Args>(args)...);
+	}
 	void destroy() {
 		if (_widget) {
 			delete _widget;