219 lines
		
	
	
	
		
			5.1 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			219 lines
		
	
	
	
		
			5.1 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/*
 | 
						|
This file is part of Telegram Desktop,
 | 
						|
the official desktop application for the Telegram messaging service.
 | 
						|
 | 
						|
For license and copyright information please follow this link:
 | 
						|
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 | 
						|
*/
 | 
						|
#include "ui/chat/pinned_bar.h"
 | 
						|
 | 
						|
#include "ui/chat/message_bar.h"
 | 
						|
#include "ui/widgets/shadow.h"
 | 
						|
#include "ui/widgets/buttons.h"
 | 
						|
#include "styles/style_chat.h"
 | 
						|
#include "styles/palette.h"
 | 
						|
 | 
						|
#include <QtGui/QtEvents>
 | 
						|
 | 
						|
namespace Ui {
 | 
						|
 | 
						|
PinnedBar::PinnedBar(
 | 
						|
	not_null<QWidget*> parent,
 | 
						|
	rpl::producer<MessageBarContent> content)
 | 
						|
: _wrap(parent, object_ptr<RpWidget>(parent))
 | 
						|
, _shadow(std::make_unique<PlainShadow>(_wrap.parentWidget())) {
 | 
						|
	_wrap.hide(anim::type::instant);
 | 
						|
	_shadow->hide();
 | 
						|
 | 
						|
	_wrap.entity()->paintRequest(
 | 
						|
	) | rpl::start_with_next([=](QRect clip) {
 | 
						|
		QPainter(_wrap.entity()).fillRect(clip, st::historyPinnedBg);
 | 
						|
	}, lifetime());
 | 
						|
	_wrap.setAttribute(Qt::WA_OpaquePaintEvent);
 | 
						|
 | 
						|
	auto copy = std::move(
 | 
						|
		content
 | 
						|
	) | rpl::start_spawning(_wrap.lifetime());
 | 
						|
 | 
						|
	rpl::duplicate(
 | 
						|
		copy
 | 
						|
	) | rpl::filter([=](const MessageBarContent &content) {
 | 
						|
		return !content.title.isEmpty() || !content.text.text.isEmpty();
 | 
						|
	}) | rpl::start_with_next([=](MessageBarContent &&content) {
 | 
						|
		const auto creating = !_bar;
 | 
						|
		if (creating) {
 | 
						|
			createControls();
 | 
						|
		}
 | 
						|
		_bar->set(std::move(content));
 | 
						|
		if (creating) {
 | 
						|
			_bar->finishAnimating();
 | 
						|
		}
 | 
						|
	}, lifetime());
 | 
						|
 | 
						|
	std::move(
 | 
						|
		copy
 | 
						|
	) | rpl::map([=](const MessageBarContent &content) {
 | 
						|
		return content.title.isEmpty() || content.text.text.isEmpty();
 | 
						|
	}) | rpl::start_with_next_done([=](bool hidden) {
 | 
						|
		_shouldBeShown = !hidden;
 | 
						|
		if (!_forceHidden) {
 | 
						|
			_wrap.toggle(_shouldBeShown, anim::type::normal);
 | 
						|
		} else if (!_shouldBeShown) {
 | 
						|
			_bar = nullptr;
 | 
						|
		}
 | 
						|
	}, [=] {
 | 
						|
		_forceHidden = true;
 | 
						|
		_wrap.toggle(false, anim::type::normal);
 | 
						|
	}, lifetime());
 | 
						|
}
 | 
						|
 | 
						|
PinnedBar::~PinnedBar() {
 | 
						|
	_rightButton.destroy();
 | 
						|
}
 | 
						|
 | 
						|
void PinnedBar::setRightButton(object_ptr<Ui::RpWidget> button) {
 | 
						|
	_rightButton.destroy();
 | 
						|
	_rightButton = std::move(button);
 | 
						|
	if (_rightButton) {
 | 
						|
		_rightButton->setParent(_wrap.entity());
 | 
						|
		_rightButton->show();
 | 
						|
	}
 | 
						|
	if (_bar) {
 | 
						|
		updateControlsGeometry(_wrap.geometry());
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
void PinnedBar::updateControlsGeometry(QRect wrapGeometry) {
 | 
						|
	_bar->widget()->resizeToWidth(
 | 
						|
		wrapGeometry.width() - (_rightButton ? _rightButton->width() : 0));
 | 
						|
	const auto hidden = _wrap.isHidden() || !wrapGeometry.height();
 | 
						|
	if (_shadow->isHidden() != hidden) {
 | 
						|
		_shadow->setVisible(!hidden);
 | 
						|
	}
 | 
						|
	if (_rightButton) {
 | 
						|
		_rightButton->moveToRight(0, 0);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
void PinnedBar::setShadowGeometryPostprocess(Fn<QRect(QRect)> postprocess) {
 | 
						|
	_shadowGeometryPostprocess = std::move(postprocess);
 | 
						|
	updateShadowGeometry(_wrap.geometry());
 | 
						|
}
 | 
						|
 | 
						|
void PinnedBar::updateShadowGeometry(QRect wrapGeometry) {
 | 
						|
	const auto regular = QRect(
 | 
						|
		wrapGeometry.x(),
 | 
						|
		wrapGeometry.y() + wrapGeometry.height(),
 | 
						|
		wrapGeometry.width(),
 | 
						|
		st::lineWidth);
 | 
						|
	_shadow->setGeometry(_shadowGeometryPostprocess
 | 
						|
		? _shadowGeometryPostprocess(regular)
 | 
						|
		: regular);
 | 
						|
}
 | 
						|
 | 
						|
void PinnedBar::createControls() {
 | 
						|
	Expects(!_bar);
 | 
						|
 | 
						|
	_bar = std::make_unique<MessageBar>(
 | 
						|
		_wrap.entity(),
 | 
						|
		st::defaultMessageBar);
 | 
						|
	if (_rightButton) {
 | 
						|
		_rightButton->raise();
 | 
						|
	}
 | 
						|
 | 
						|
	// Clicks.
 | 
						|
	_bar->widget()->setCursor(style::cur_pointer);
 | 
						|
	_bar->widget()->events(
 | 
						|
	) | rpl::filter([=](not_null<QEvent*> event) {
 | 
						|
		return (event->type() == QEvent::MouseButtonPress);
 | 
						|
	}) | rpl::map([=] {
 | 
						|
		return _bar->widget()->events(
 | 
						|
		) | rpl::filter([=](not_null<QEvent*> event) {
 | 
						|
			return (event->type() == QEvent::MouseButtonRelease);
 | 
						|
		}) | rpl::take(1) | rpl::filter([=](not_null<QEvent*> event) {
 | 
						|
			return _bar->widget()->rect().contains(
 | 
						|
				static_cast<QMouseEvent*>(event.get())->pos());
 | 
						|
		});
 | 
						|
	}) | rpl::flatten_latest(
 | 
						|
	) | rpl::to_empty | rpl::start_to_stream(
 | 
						|
		_barClicks,
 | 
						|
		_bar->widget()->lifetime());
 | 
						|
 | 
						|
	_bar->widget()->move(0, 0);
 | 
						|
	_bar->widget()->show();
 | 
						|
	_wrap.entity()->resize(
 | 
						|
		_wrap.entity()->width(),
 | 
						|
		_bar->widget()->height());
 | 
						|
 | 
						|
	_wrap.geometryValue(
 | 
						|
	) | rpl::start_with_next([=](QRect rect) {
 | 
						|
		updateShadowGeometry(rect);
 | 
						|
		updateControlsGeometry(rect);
 | 
						|
	}, _bar->widget()->lifetime());
 | 
						|
 | 
						|
	_wrap.shownValue(
 | 
						|
	) | rpl::skip(
 | 
						|
		1
 | 
						|
	) | rpl::filter([=](bool shown) {
 | 
						|
		return !shown && !_forceHidden;
 | 
						|
	}) | rpl::start_with_next([=] {
 | 
						|
		_bar = nullptr;
 | 
						|
	}, _bar->widget()->lifetime());
 | 
						|
 | 
						|
	Ensures(_bar != nullptr);
 | 
						|
}
 | 
						|
 | 
						|
void PinnedBar::show() {
 | 
						|
	if (!_forceHidden) {
 | 
						|
		return;
 | 
						|
	}
 | 
						|
	_forceHidden = false;
 | 
						|
	if (_shouldBeShown) {
 | 
						|
		_wrap.show(anim::type::instant);
 | 
						|
		_shadow->show();
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
void PinnedBar::hide() {
 | 
						|
	if (_forceHidden) {
 | 
						|
		return;
 | 
						|
	}
 | 
						|
	_forceHidden = true;
 | 
						|
	_wrap.hide(anim::type::instant);
 | 
						|
	_shadow->hide();
 | 
						|
}
 | 
						|
 | 
						|
void PinnedBar::raise() {
 | 
						|
	_wrap.raise();
 | 
						|
	_shadow->raise();
 | 
						|
}
 | 
						|
 | 
						|
void PinnedBar::finishAnimating() {
 | 
						|
	_wrap.finishAnimating();
 | 
						|
}
 | 
						|
 | 
						|
void PinnedBar::move(int x, int y) {
 | 
						|
	_wrap.move(x, y);
 | 
						|
}
 | 
						|
 | 
						|
void PinnedBar::resizeToWidth(int width) {
 | 
						|
	_wrap.entity()->resizeToWidth(width);
 | 
						|
}
 | 
						|
 | 
						|
int PinnedBar::height() const {
 | 
						|
	return !_forceHidden
 | 
						|
		? _wrap.height()
 | 
						|
		: _shouldBeShown
 | 
						|
		? st::historyReplyHeight
 | 
						|
		: 0;
 | 
						|
}
 | 
						|
 | 
						|
rpl::producer<int> PinnedBar::heightValue() const {
 | 
						|
	return _wrap.heightValue();
 | 
						|
}
 | 
						|
 | 
						|
rpl::producer<> PinnedBar::barClicks() const {
 | 
						|
	return _barClicks.events();
 | 
						|
}
 | 
						|
 | 
						|
} // namespace Ui
 |