427 lines
		
	
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			427 lines
		
	
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | |
| 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 "title.h"
 | |
| 
 | |
| #include "lang.h"
 | |
| #include "mainwidget.h"
 | |
| #include "mainwindow.h"
 | |
| #include "application.h"
 | |
| #include "boxes/contactsbox.h"
 | |
| #include "boxes/aboutbox.h"
 | |
| #include "media/media_audio.h"
 | |
| #include "media/player/media_player_title_button.h"
 | |
| #include "media/player/media_player_panel.h"
 | |
| #include "media/player/media_player_instance.h"
 | |
| #include "styles/style_window.h"
 | |
| 
 | |
| 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 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 TitleWidget::Hider::mousePressEvent(QMouseEvent *e) {
 | |
| 	if (e->button() == Qt::LeftButton && _callback) {
 | |
| 		_callback();
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void TitleWidget::Hider::setLevel(float64 level) {
 | |
| 	_level = level;
 | |
| 	update();
 | |
| }
 | |
| 
 | |
| 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)
 | |
| , _lock(this)
 | |
| , _update(this)
 | |
| , _minimize(this)
 | |
| , _maximize(this)
 | |
| , _restore(this)
 | |
| , _close(this)
 | |
| , _a_update(animation(this, &TitleWidget::step_update))
 | |
| , lastMaximized(!(parent->windowState() & Qt::WindowMaximized)) {
 | |
| 	setGeometry(0, 0, parent->width(), st::titleHeight);
 | |
| 	setAttribute(Qt::WA_OpaquePaintEvent);
 | |
| 
 | |
| 	onWindowStateChanged();
 | |
| 	updateControlsVisibility();
 | |
| 
 | |
| 	connect(&_cancel, SIGNAL(clicked()), this, SIGNAL(hiderClicked()));
 | |
| 	connect(&_settings, SIGNAL(clicked()), parent, SLOT(showSettings()));
 | |
| 	connect(&_contacts, SIGNAL(clicked()), this, SLOT(onContacts()));
 | |
| 	connect(&_about, SIGNAL(clicked()), this, SLOT(onAbout()));
 | |
| 	connect(parent->windowHandle(), SIGNAL(windowStateChanged(Qt::WindowState)), this, SLOT(onWindowStateChanged(Qt::WindowState)));
 | |
| 
 | |
| #ifndef TDESKTOP_DISABLE_AUTOUPDATE
 | |
| 	Sandbox::connect(SIGNAL(updateReady()), this, SLOT(updateControlsVisibility()));
 | |
| #endif // !TDESKTOP_DISABLE_AUTOUPDATE
 | |
| 
 | |
| 	subscribe(Adaptive::Changed(), [this]() { updateAdaptiveLayout(); });
 | |
| 	if (Media::Player::exists()) {
 | |
| 		subscribe(Media::Player::instance()->usePanelPlayer(), [this](bool usePanel) {
 | |
| 			updatePlayerButton(usePanel);
 | |
| 		});
 | |
| 	}
 | |
| 
 | |
|     if (cPlatform() != dbipWindows) {
 | |
|         _minimize.hide();
 | |
|         _maximize.hide();
 | |
|         _restore.hide();
 | |
|         _close.hide();
 | |
|     }
 | |
| }
 | |
| 
 | |
| void TitleWidget::paintEvent(QPaintEvent *e) {
 | |
| 	Painter p(this);
 | |
| 	p.fillRect(rect(), st::titleBg);
 | |
| 	if (!_cancel.isHidden()) {
 | |
| 		p.setPen(st::titleTextButton.color);
 | |
| 		p.setFont(st::titleTextButton.font);
 | |
| 		bool inlineSwitchChoose = (App::main() && App::main()->selectingPeerForInlineSwitch());
 | |
| 		auto chooseText = lang(inlineSwitchChoose ? lng_inline_switch_choose : lng_forward_choose);
 | |
| 		p.drawText(st::titleMenuOffset - st::titleTextButton.width / 2, st::titleTextButton.textTop + st::titleTextButton.font->ascent, chooseText);
 | |
| 	}
 | |
| 	st::titleIcon.paint(p, st::titleIconPosition, width());
 | |
| 	if (Adaptive::OneColumn() && !_counter.isNull() && App::main()) {
 | |
| 		p.drawPixmap(st::titleCounterPosition, _counter);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void TitleWidget::step_update(float64 ms, bool timer) {
 | |
| 	float64 phase = sin(M_PI_2 * (ms / st::updateBlinkDuration));
 | |
| 	if (phase < 0) phase = -phase;
 | |
| 	_update.setOverLevel(phase);
 | |
| }
 | |
| 
 | |
| void TitleWidget::setHideLevel(float64 level) {
 | |
| 	if (level != hideLevel) {
 | |
| 		hideLevel = level;
 | |
| 		if (hideLevel) {
 | |
| 			if (!_hider) {
 | |
| 				_hider.create(this);
 | |
| 				_hider->setGeometry(rect());
 | |
| 				_hider->setClickedCallback([this]() { emit hiderClicked(); });
 | |
| 				_hider->setVisible(!Adaptive::OneColumn());
 | |
| 			}
 | |
| 			_hider->setLevel(hideLevel);
 | |
| 		} else {
 | |
| 			if (_hider) {
 | |
| 				_hider.destroyDelayed();
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void TitleWidget::onContacts() {
 | |
| 	if (App::wnd() && App::wnd()->isHidden()) App::wnd()->showFromTray();
 | |
| 
 | |
| 	if (!App::self()) return;
 | |
| 	Ui::showLayer(new ContactsBox());
 | |
| }
 | |
| 
 | |
| void TitleWidget::onAbout() {
 | |
| 	if (App::wnd() && App::wnd()->isHidden()) App::wnd()->showFromTray();
 | |
| 	Ui::showLayer(new AboutBox());
 | |
| }
 | |
| 
 | |
| void TitleWidget::updatePlayerButton(bool usePanel) {
 | |
| 	if (usePanel && !_player) {
 | |
| 		_player.create(this);
 | |
| 	} else if (!usePanel && _player) {
 | |
| 		_player.destroyDelayed();
 | |
| 	}
 | |
| 	updateControlsVisibility();
 | |
| }
 | |
| 
 | |
| void TitleWidget::updateControlsPosition() {
 | |
| 	QPoint p(width() - ((cPlatform() == dbipWindows && lastMaximized) ? 0 : st::sysBtnDelta), 0);
 | |
| 
 | |
| 	if (!_update.isHidden()) {
 | |
| 		p.setX(p.x() - _update.width());
 | |
| 		_update.move(p);
 | |
| 		if (!_lock.isHidden()) {
 | |
| 			p.setX(p.x() - _lock.width());
 | |
| 			_lock.move(p);
 | |
| 			p.setX(p.x() + _lock.width());
 | |
| 		}
 | |
| 		p.setX(p.x() + _update.width());
 | |
| 	}
 | |
| 	_cancel.move(p.x() - _cancel.width(), 0);
 | |
| 
 | |
| 	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() - _minimize.width());
 | |
| 		_minimize.move(p);
 | |
| 	}
 | |
| 	if (_update.isHidden() && !_lock.isHidden()) {
 | |
| 		p.setX(p.x() - _lock.width());
 | |
| 		_lock.move(p);
 | |
| 	}
 | |
| 	if (_player) {
 | |
| 		p.setX(p.x() - _player->width());
 | |
| 		_player->move(p);
 | |
| 	}
 | |
| 
 | |
| 	_settings.move(st::titleMenuOffset, 0);
 | |
| 	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);
 | |
| 	}
 | |
| 
 | |
| 	if (_hider) {
 | |
| 		_hider->resize(size());
 | |
| 	}
 | |
| }
 | |
| 
 | |
| 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 // !TDESKTOP_DISABLE_AUTOUPDATE
 | |
| 	bool updateReady = false;
 | |
| #endif // else for !TDESKTOP_DISABLE_AUTOUPDATE
 | |
| 	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() {
 | |
| 	updateControlsVisibility();
 | |
| 	if (Adaptive::OneColumn()) {
 | |
| 		updateCounter();
 | |
| 	}
 | |
| 	if (_hider) {
 | |
| 		_hider->setVisible(!Adaptive::OneColumn());
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void TitleWidget::updateCounter() {
 | |
| 	if (!Adaptive::OneColumn() || !MTP::authedId()) return;
 | |
| 
 | |
| 	int32 counter = App::histories().unreadBadge();
 | |
| 	bool muted = App::histories().unreadOnlyMuted();
 | |
| 
 | |
| 	style::color bg = muted ? st::counterMuteBG : st::counterBG;
 | |
| 
 | |
| 	if (counter > 0) {
 | |
| 		int32 size = cRetina() ? -32 : -16;
 | |
| 		switch (cScale()) {
 | |
| 		case dbisOneAndQuarter: size = -20; break;
 | |
| 		case dbisOneAndHalf: size = -24; break;
 | |
| 		case dbisTwo: size = -32; break;
 | |
| 		}
 | |
| 		_counter = App::pixmapFromImageInPlace(App::wnd()->iconWithCounter(size, counter, bg, false));
 | |
| 		_counter.setDevicePixelRatio(cRetinaFactor());
 | |
| 		update(QRect(st::titleCounterPosition, _counter.size() / cIntRetinaFactor()));
 | |
| 	} else {
 | |
| 		if (!_counter.isNull()) {
 | |
| 			update(QRect(st::titleCounterPosition, _counter.size() / cIntRetinaFactor()));
 | |
| 			_counter = QPixmap();
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void TitleWidget::mousePressEvent(QMouseEvent *e) {
 | |
| 	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 (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);
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void TitleWidget::onWindowStateChanged(Qt::WindowState state) {
 | |
| 	if (state == Qt::WindowMinimized) return;
 | |
| 	maximizedChanged(state == Qt::WindowMaximized);
 | |
| }
 | |
| 
 | |
| void TitleWidget::maximizedChanged(bool maximized, bool force) {
 | |
| 	if (lastMaximized == maximized && !force) return;
 | |
| 
 | |
| 	lastMaximized = maximized;
 | |
| 
 | |
|     if (cPlatform() != dbipWindows || !_update.isHidden()) return;
 | |
| 	if (maximized) {
 | |
| 		_maximize.clearState();
 | |
| 	} else {
 | |
| 		_restore.clearState();
 | |
| 	}
 | |
| 
 | |
| 	_maximize.setVisible(!maximized);
 | |
| 	_restore.setVisible(maximized);
 | |
| 
 | |
| 	updateControlsPosition();
 | |
| }
 | |
| 
 | |
| HitTestType TitleWidget::hitTest(const QPoint &p) {
 | |
| 	if (App::wnd() && Ui::isLayerShown()) return HitTestType::None;
 | |
| 
 | |
| 	int x(p.x()), y(p.y()), w(width()), h(height());
 | |
| 	if (!Adaptive::OneColumn() && _hider && x >= App::main()->dlgsWidth()) return HitTestType::None;
 | |
| 
 | |
| 	if (x >= st::titleIconPosition.x() && y >= st::titleIconPosition.y() && x < st::titleIconPosition.x() + st::titleIcon.width() && y < st::titleIconPosition.y() + st::titleIcon.height()) {
 | |
| 		return HitTestType::Icon;
 | |
| 	} else if (false
 | |
| 		|| (_player && _player->geometry().contains(p))
 | |
| 		|| (_lock.hitTest(p - _lock.geometry().topLeft()) == HitTestType::SysButton && _lock.isVisible())
 | |
| 		|| (_update.hitTest(p - _update.geometry().topLeft()) == HitTestType::SysButton && _update.isVisible())
 | |
| 		|| (_minimize.hitTest(p - _minimize.geometry().topLeft()) == HitTestType::SysButton)
 | |
| 		|| (_maximize.hitTest(p - _maximize.geometry().topLeft()) == HitTestType::SysButton)
 | |
| 		|| (_restore.hitTest(p - _restore.geometry().topLeft()) == HitTestType::SysButton)
 | |
| 		|| (_close.hitTest(p - _close.geometry().topLeft()) == HitTestType::SysButton)
 | |
| 	) {
 | |
| 		return HitTestType::SysButton;
 | |
| 	} else if (x >= 0 && x < w && y >= 0 && y < h) {
 | |
| 		if (false
 | |
| 			|| (!_cancel.isHidden() && _cancel.geometry().contains(x, y))
 | |
| 			|| (!_settings.isHidden() && _settings.geometry().contains(x, y))
 | |
| 			|| (!_contacts.isHidden() && _contacts.geometry().contains(x, y))
 | |
| 			|| (!_about.isHidden() && _about.geometry().contains(x, y))
 | |
| 		) {
 | |
| 			return HitTestType::Client;
 | |
| 		}
 | |
| 		return HitTestType::Caption;
 | |
| 	}
 | |
| 	return HitTestType::None;
 | |
| }
 | |
| 
 | |
| QRect TitleWidget::iconRect() const {
 | |
| 	return myrtlrect(QRect(st::titleIconPosition, st::titleIcon.size()));
 | |
| }
 | 
