mirror of https://github.com/procxx/kepka.git
				
				
				
			Remove Qt MOC dependency for tabbed selector.
This commit is contained in:
		
							parent
							
								
									9f5b09c263
								
							
						
					
					
						commit
						ef4f0168f8
					
				|  | @ -32,6 +32,73 @@ constexpr auto kStickersPanelPerRow = 5; | ||||||
| 
 | 
 | ||||||
| } // namespace
 | } // namespace
 | ||||||
| 
 | 
 | ||||||
|  | class StickerSetBox::Inner : public TWidget, public RPCSender, private base::Subscriber { | ||||||
|  | public: | ||||||
|  | 	Inner(QWidget *parent, const MTPInputStickerSet &set); | ||||||
|  | 
 | ||||||
|  | 	bool loaded() const; | ||||||
|  | 	bool notInstalled() const; | ||||||
|  | 	bool official() const; | ||||||
|  | 	Fn<TextWithEntities()> title() const; | ||||||
|  | 	QString shortName() const; | ||||||
|  | 
 | ||||||
|  | 	void install(); | ||||||
|  | 	rpl::producer<uint64> setInstalled() const; | ||||||
|  | 	rpl::producer<> updateControls() const; | ||||||
|  | 
 | ||||||
|  | 	~Inner(); | ||||||
|  | 
 | ||||||
|  | protected: | ||||||
|  | 	void mousePressEvent(QMouseEvent *e) override; | ||||||
|  | 	void mouseMoveEvent(QMouseEvent *e) override; | ||||||
|  | 	void mouseReleaseEvent(QMouseEvent *e) override; | ||||||
|  | 	void paintEvent(QPaintEvent *e) override; | ||||||
|  | 	void leaveEventHook(QEvent *e) override; | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  | 	void updateSelected(); | ||||||
|  | 	void setSelected(int selected); | ||||||
|  | 	void startOverAnimation(int index, float64 from, float64 to); | ||||||
|  | 	int stickerFromGlobalPos(const QPoint &p) const; | ||||||
|  | 
 | ||||||
|  | 	void gotSet(const MTPmessages_StickerSet &set); | ||||||
|  | 	bool failedSet(const RPCError &error); | ||||||
|  | 
 | ||||||
|  | 	void installDone(const MTPmessages_StickerSetInstallResult &result); | ||||||
|  | 	bool installFail(const RPCError &error); | ||||||
|  | 
 | ||||||
|  | 	bool isMasksSet() const { | ||||||
|  | 		return (_setFlags & MTPDstickerSet::Flag::f_masks); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	void showPreview(); | ||||||
|  | 
 | ||||||
|  | 	std::vector<Animation> _packOvers; | ||||||
|  | 	Stickers::Pack _pack; | ||||||
|  | 	Stickers::ByEmojiMap _emoji; | ||||||
|  | 	bool _loaded = false; | ||||||
|  | 	uint64 _setId = 0; | ||||||
|  | 	uint64 _setAccess = 0; | ||||||
|  | 	QString _setTitle, _setShortName; | ||||||
|  | 	int _setCount = 0; | ||||||
|  | 	int32 _setHash = 0; | ||||||
|  | 	MTPDstickerSet::Flags _setFlags = 0; | ||||||
|  | 	TimeId _setInstallDate = TimeId(0); | ||||||
|  | 
 | ||||||
|  | 	MTPInputStickerSet _input; | ||||||
|  | 
 | ||||||
|  | 	mtpRequestId _installRequest = 0; | ||||||
|  | 
 | ||||||
|  | 	int _selected = -1; | ||||||
|  | 
 | ||||||
|  | 	base::Timer _previewTimer; | ||||||
|  | 	int _previewShown = -1; | ||||||
|  | 
 | ||||||
|  | 	rpl::event_stream<uint64> _setInstalled; | ||||||
|  | 	rpl::event_stream<> _updateControls; | ||||||
|  | 
 | ||||||
|  | }; | ||||||
|  | 
 | ||||||
| StickerSetBox::StickerSetBox(QWidget*, const MTPInputStickerSet &set) | StickerSetBox::StickerSetBox(QWidget*, const MTPInputStickerSet &set) | ||||||
| : _set(set) { | : _set(set) { | ||||||
| } | } | ||||||
|  | @ -41,33 +108,37 @@ void StickerSetBox::prepare() { | ||||||
| 
 | 
 | ||||||
| 	_inner = setInnerWidget(object_ptr<Inner>(this, _set), st::stickersScroll); | 	_inner = setInnerWidget(object_ptr<Inner>(this, _set), st::stickersScroll); | ||||||
| 	Auth().data().stickersUpdated( | 	Auth().data().stickersUpdated( | ||||||
| 	) | rpl::start_with_next( | 	) | rpl::start_with_next([=] { | ||||||
| 		[this] { updateButtons(); }, | 		updateButtons(); | ||||||
| 		lifetime()); | 	}, lifetime()); | ||||||
| 
 | 
 | ||||||
| 	setDimensions(st::boxWideWidth, st::stickersMaxHeight); | 	setDimensions(st::boxWideWidth, st::stickersMaxHeight); | ||||||
| 
 | 
 | ||||||
| 	onUpdateButtons(); | 	updateTitleAndButtons(); | ||||||
|  | 
 | ||||||
|  | 	_inner->updateControls( | ||||||
|  | 	) | rpl::start_with_next([=] { | ||||||
|  | 		updateTitleAndButtons(); | ||||||
|  | 	}, lifetime()); | ||||||
| 
 | 
 | ||||||
| 	connect(_inner, SIGNAL(updateButtons()), this, SLOT(onUpdateButtons())); |  | ||||||
| 	_inner->setInstalled( | 	_inner->setInstalled( | ||||||
| 	) | rpl::start_with_next([this](auto &&setId) { | 	) | rpl::start_with_next([=](uint64 setId) { | ||||||
| 		Auth().api().stickerSetInstalled(setId); | 		Auth().api().stickerSetInstalled(setId); | ||||||
| 		this->closeBox(); | 		closeBox(); | ||||||
| 	}, lifetime()); | 	}, lifetime()); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void StickerSetBox::onAddStickers() { | void StickerSetBox::addStickers() { | ||||||
| 	_inner->install(); | 	_inner->install(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void StickerSetBox::onShareStickers() { | void StickerSetBox::shareStickers() { | ||||||
| 	auto url = Messenger::Instance().createInternalLinkFull(qsl("addstickers/") + _inner->shortName()); | 	auto url = Messenger::Instance().createInternalLinkFull(qsl("addstickers/") + _inner->shortName()); | ||||||
| 	QApplication::clipboard()->setText(url); | 	QApplication::clipboard()->setText(url); | ||||||
| 	Ui::show(Box<InformBox>(lang(lng_stickers_copied))); | 	Ui::show(Box<InformBox>(lang(lng_stickers_copied))); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void StickerSetBox::onUpdateButtons() { | void StickerSetBox::updateTitleAndButtons() { | ||||||
| 	setTitle(_inner->title()); | 	setTitle(_inner->title()); | ||||||
| 	updateButtons(); | 	updateButtons(); | ||||||
| } | } | ||||||
|  | @ -76,16 +147,16 @@ void StickerSetBox::updateButtons() { | ||||||
| 	clearButtons(); | 	clearButtons(); | ||||||
| 	if (_inner->loaded()) { | 	if (_inner->loaded()) { | ||||||
| 		if (_inner->notInstalled()) { | 		if (_inner->notInstalled()) { | ||||||
| 			addButton(langFactory(lng_stickers_add_pack), [this] { onAddStickers(); }); | 			addButton(langFactory(lng_stickers_add_pack), [=] { addStickers(); }); | ||||||
| 			addButton(langFactory(lng_cancel), [this] { closeBox(); }); | 			addButton(langFactory(lng_cancel), [=] { closeBox(); }); | ||||||
| 		} else if (_inner->official()) { | 		} else if (_inner->official()) { | ||||||
| 			addButton(langFactory(lng_about_done), [this] { closeBox(); }); | 			addButton(langFactory(lng_about_done), [=] { closeBox(); }); | ||||||
| 		} else { | 		} else { | ||||||
| 			addButton(langFactory(lng_stickers_share_pack), [this] { onShareStickers(); }); | 			addButton(langFactory(lng_stickers_share_pack), [=] { shareStickers(); }); | ||||||
| 			addButton(langFactory(lng_cancel), [this] { closeBox(); }); | 			addButton(langFactory(lng_cancel), [=] { closeBox(); }); | ||||||
| 		} | 		} | ||||||
| 	} else { | 	} else { | ||||||
| 		addButton(langFactory(lng_cancel), [this] { closeBox(); }); | 		addButton(langFactory(lng_cancel), [=] { closeBox(); }); | ||||||
| 	} | 	} | ||||||
| 	update(); | 	update(); | ||||||
| } | } | ||||||
|  | @ -96,7 +167,8 @@ void StickerSetBox::resizeEvent(QResizeEvent *e) { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| StickerSetBox::Inner::Inner(QWidget *parent, const MTPInputStickerSet &set) : TWidget(parent) | StickerSetBox::Inner::Inner(QWidget *parent, const MTPInputStickerSet &set) : TWidget(parent) | ||||||
| , _input(set) { | , _input(set) | ||||||
|  | , _previewTimer([=] { showPreview(); }) { | ||||||
| 	switch (set.type()) { | 	switch (set.type()) { | ||||||
| 	case mtpc_inputStickerSetID: _setId = set.c_inputStickerSetID().vid.v; _setAccess = set.c_inputStickerSetID().vaccess_hash.v; break; | 	case mtpc_inputStickerSetID: _setId = set.c_inputStickerSetID().vid.v; _setAccess = set.c_inputStickerSetID().vaccess_hash.v; break; | ||||||
| 	case mtpc_inputStickerSetShortName: _setShortName = qs(set.c_inputStickerSetShortName().vshort_name); break; | 	case mtpc_inputStickerSetShortName: _setShortName = qs(set.c_inputStickerSetShortName().vshort_name); break; | ||||||
|  | @ -107,9 +179,6 @@ StickerSetBox::Inner::Inner(QWidget *parent, const MTPInputStickerSet &set) : TW | ||||||
| 	subscribe(Auth().downloaderTaskFinished(), [this] { update(); }); | 	subscribe(Auth().downloaderTaskFinished(), [this] { update(); }); | ||||||
| 
 | 
 | ||||||
| 	setMouseTracking(true); | 	setMouseTracking(true); | ||||||
| 
 |  | ||||||
| 	_previewTimer.setSingleShot(true); |  | ||||||
| 	connect(&_previewTimer, SIGNAL(timeout()), this, SLOT(onPreview())); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void StickerSetBox::Inner::gotSet(const MTPmessages_StickerSet &set) { | void StickerSetBox::Inner::gotSet(const MTPmessages_StickerSet &set) { | ||||||
|  | @ -183,8 +252,15 @@ void StickerSetBox::Inner::gotSet(const MTPmessages_StickerSet &set) { | ||||||
| 	_loaded = true; | 	_loaded = true; | ||||||
| 
 | 
 | ||||||
| 	updateSelected(); | 	updateSelected(); | ||||||
|  | 	_updateControls.fire({}); | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| 	emit updateButtons(); | rpl::producer<uint64> StickerSetBox::Inner::setInstalled() const { | ||||||
|  | 	return _setInstalled.events(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | rpl::producer<> StickerSetBox::Inner::updateControls() const { | ||||||
|  | 	return _updateControls.events(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool StickerSetBox::Inner::failedSet(const RPCError &error) { | bool StickerSetBox::Inner::failedSet(const RPCError &error) { | ||||||
|  | @ -273,7 +349,7 @@ bool StickerSetBox::Inner::installFail(const RPCError &error) { | ||||||
| void StickerSetBox::Inner::mousePressEvent(QMouseEvent *e) { | void StickerSetBox::Inner::mousePressEvent(QMouseEvent *e) { | ||||||
| 	int index = stickerFromGlobalPos(e->globalPos()); | 	int index = stickerFromGlobalPos(e->globalPos()); | ||||||
| 	if (index >= 0 && index < _pack.size()) { | 	if (index >= 0 && index < _pack.size()) { | ||||||
| 		_previewTimer.start(QApplication::startDragTime()); | 		_previewTimer.callOnce(QApplication::startDragTime()); | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -300,10 +376,10 @@ void StickerSetBox::Inner::mouseReleaseEvent(QMouseEvent *e) { | ||||||
| 		return; | 		return; | ||||||
| 	} | 	} | ||||||
| 	if (_previewTimer.isActive()) { | 	if (_previewTimer.isActive()) { | ||||||
| 		_previewTimer.stop(); | 		_previewTimer.cancel(); | ||||||
| 		int index = stickerFromGlobalPos(e->globalPos()); | 		const auto index = stickerFromGlobalPos(e->globalPos()); | ||||||
| 		if (index >= 0 && index < _pack.size() && !isMasksSet()) { | 		if (index >= 0 && index < _pack.size() && !isMasksSet()) { | ||||||
| 			if (auto main = App::main()) { | 			if (const auto main = App::main()) { | ||||||
| 				if (main->onSendSticker(_pack[index])) { | 				if (main->onSendSticker(_pack[index])) { | ||||||
| 					Ui::hideSettingsAndLayer(); | 					Ui::hideSettingsAndLayer(); | ||||||
| 				} | 				} | ||||||
|  | @ -338,7 +414,7 @@ void StickerSetBox::Inner::startOverAnimation(int index, float64 from, float64 t | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void StickerSetBox::Inner::onPreview() { | void StickerSetBox::Inner::showPreview() { | ||||||
| 	int index = stickerFromGlobalPos(QCursor::pos()); | 	int index = stickerFromGlobalPos(QCursor::pos()); | ||||||
| 	if (index >= 0 && index < _pack.size()) { | 	if (index >= 0 && index < _pack.size()) { | ||||||
| 		_previewShown = index; | 		_previewShown = index; | ||||||
|  |  | ||||||
|  | @ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| #include "boxes/abstract_box.h" | #include "boxes/abstract_box.h" | ||||||
|  | #include "base/timer.h" | ||||||
| #include "chat_helpers/stickers.h" | #include "chat_helpers/stickers.h" | ||||||
| 
 | 
 | ||||||
| class ConfirmBox; | class ConfirmBox; | ||||||
|  | @ -17,8 +18,6 @@ class PlainShadow; | ||||||
| } // namespace Ui
 | } // namespace Ui
 | ||||||
| 
 | 
 | ||||||
| class StickerSetBox : public BoxContent, public RPCSender { | class StickerSetBox : public BoxContent, public RPCSender { | ||||||
| 	Q_OBJECT |  | ||||||
| 
 |  | ||||||
| public: | public: | ||||||
| 	StickerSetBox(QWidget*, const MTPInputStickerSet &set); | 	StickerSetBox(QWidget*, const MTPInputStickerSet &set); | ||||||
| 
 | 
 | ||||||
|  | @ -27,13 +26,11 @@ protected: | ||||||
| 
 | 
 | ||||||
| 	void resizeEvent(QResizeEvent *e) override; | 	void resizeEvent(QResizeEvent *e) override; | ||||||
| 
 | 
 | ||||||
| private slots: |  | ||||||
| 	void onAddStickers(); |  | ||||||
| 	void onShareStickers(); |  | ||||||
| 	void onUpdateButtons(); |  | ||||||
| 
 |  | ||||||
| private: | private: | ||||||
|  | 	void updateTitleAndButtons(); | ||||||
| 	void updateButtons(); | 	void updateButtons(); | ||||||
|  | 	void addStickers(); | ||||||
|  | 	void shareStickers(); | ||||||
| 
 | 
 | ||||||
| 	MTPInputStickerSet _set; | 	MTPInputStickerSet _set; | ||||||
| 
 | 
 | ||||||
|  | @ -41,77 +38,3 @@ private: | ||||||
| 	QPointer<Inner> _inner; | 	QPointer<Inner> _inner; | ||||||
| 
 | 
 | ||||||
| }; | }; | ||||||
| 
 |  | ||||||
| // This class is hold in header because it requires Qt preprocessing.
 |  | ||||||
| class StickerSetBox::Inner : public TWidget, public RPCSender, private base::Subscriber { |  | ||||||
| 	Q_OBJECT |  | ||||||
| 
 |  | ||||||
| public: |  | ||||||
| 	Inner(QWidget *parent, const MTPInputStickerSet &set); |  | ||||||
| 
 |  | ||||||
| 	bool loaded() const; |  | ||||||
| 	bool notInstalled() const; |  | ||||||
| 	bool official() const; |  | ||||||
| 	Fn<TextWithEntities()> title() const; |  | ||||||
| 	QString shortName() const; |  | ||||||
| 
 |  | ||||||
| 	void install(); |  | ||||||
| 	auto setInstalled() const { |  | ||||||
| 		return _setInstalled.events(); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	~Inner(); |  | ||||||
| 
 |  | ||||||
| protected: |  | ||||||
| 	void mousePressEvent(QMouseEvent *e) override; |  | ||||||
| 	void mouseMoveEvent(QMouseEvent *e) override; |  | ||||||
| 	void mouseReleaseEvent(QMouseEvent *e) override; |  | ||||||
| 	void paintEvent(QPaintEvent *e) override; |  | ||||||
| 	void leaveEventHook(QEvent *e) override; |  | ||||||
| 
 |  | ||||||
| private slots: |  | ||||||
| 	void onPreview(); |  | ||||||
| 
 |  | ||||||
| signals: |  | ||||||
| 	void updateButtons(); |  | ||||||
| 
 |  | ||||||
| private: |  | ||||||
| 	void updateSelected(); |  | ||||||
| 	void setSelected(int selected); |  | ||||||
| 	void startOverAnimation(int index, float64 from, float64 to); |  | ||||||
| 	int stickerFromGlobalPos(const QPoint &p) const; |  | ||||||
| 
 |  | ||||||
| 	void gotSet(const MTPmessages_StickerSet &set); |  | ||||||
| 	bool failedSet(const RPCError &error); |  | ||||||
| 
 |  | ||||||
| 	void installDone(const MTPmessages_StickerSetInstallResult &result); |  | ||||||
| 	bool installFail(const RPCError &error); |  | ||||||
| 
 |  | ||||||
| 	bool isMasksSet() const { |  | ||||||
| 		return (_setFlags & MTPDstickerSet::Flag::f_masks); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	std::vector<Animation> _packOvers; |  | ||||||
| 	Stickers::Pack _pack; |  | ||||||
| 	Stickers::ByEmojiMap _emoji; |  | ||||||
| 	bool _loaded = false; |  | ||||||
| 	uint64 _setId = 0; |  | ||||||
| 	uint64 _setAccess = 0; |  | ||||||
| 	QString _setTitle, _setShortName; |  | ||||||
| 	int _setCount = 0; |  | ||||||
| 	int32 _setHash = 0; |  | ||||||
| 	MTPDstickerSet::Flags _setFlags = 0; |  | ||||||
| 	TimeId _setInstallDate = TimeId(0); |  | ||||||
| 
 |  | ||||||
| 	MTPInputStickerSet _input; |  | ||||||
| 
 |  | ||||||
| 	mtpRequestId _installRequest = 0; |  | ||||||
| 
 |  | ||||||
| 	int _selected = -1; |  | ||||||
| 
 |  | ||||||
| 	QTimer _previewTimer; |  | ||||||
| 	int _previewShown = -1; |  | ||||||
| 
 |  | ||||||
| 	rpl::event_stream<uint64> _setInstalled; |  | ||||||
| 
 |  | ||||||
| }; |  | ||||||
|  |  | ||||||
|  | @ -18,6 +18,57 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL | ||||||
| 
 | 
 | ||||||
| namespace ChatHelpers { | namespace ChatHelpers { | ||||||
| 
 | 
 | ||||||
|  | class EmojiColorPicker : public Ui::RpWidget { | ||||||
|  | public: | ||||||
|  | 	EmojiColorPicker(QWidget *parent); | ||||||
|  | 
 | ||||||
|  | 	void showEmoji(EmojiPtr emoji); | ||||||
|  | 
 | ||||||
|  | 	void clearSelection(); | ||||||
|  | 	void handleMouseMove(QPoint globalPos); | ||||||
|  | 	void handleMouseRelease(QPoint globalPos); | ||||||
|  | 	void setSingleSize(QSize size); | ||||||
|  | 
 | ||||||
|  | 	void showAnimated(); | ||||||
|  | 	void hideAnimated(); | ||||||
|  | 	void hideFast(); | ||||||
|  | 
 | ||||||
|  | 	rpl::producer<EmojiPtr> chosen() const; | ||||||
|  | 	rpl::producer<> hidden() const; | ||||||
|  | 
 | ||||||
|  | protected: | ||||||
|  | 	void paintEvent(QPaintEvent *e) override; | ||||||
|  | 	void mousePressEvent(QMouseEvent *e) override; | ||||||
|  | 	void mouseReleaseEvent(QMouseEvent *e) override; | ||||||
|  | 	void mouseMoveEvent(QMouseEvent *e) override; | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  | 	void animationCallback(); | ||||||
|  | 	void updateSize(); | ||||||
|  | 
 | ||||||
|  | 	void drawVariant(Painter &p, int variant); | ||||||
|  | 
 | ||||||
|  | 	void updateSelected(); | ||||||
|  | 	void setSelected(int newSelected); | ||||||
|  | 
 | ||||||
|  | 	bool _ignoreShow = false; | ||||||
|  | 
 | ||||||
|  | 	QVector<EmojiPtr> _variants; | ||||||
|  | 
 | ||||||
|  | 	int _selected = -1; | ||||||
|  | 	int _pressedSel = -1; | ||||||
|  | 	QPoint _lastMousePos; | ||||||
|  | 	QSize _singleSize; | ||||||
|  | 
 | ||||||
|  | 	bool _hiding = false; | ||||||
|  | 	QPixmap _cache; | ||||||
|  | 	Animation _a_opacity; | ||||||
|  | 
 | ||||||
|  | 	rpl::event_stream<EmojiPtr> _chosen; | ||||||
|  | 	rpl::event_stream<> _hidden; | ||||||
|  | 
 | ||||||
|  | }; | ||||||
|  | 
 | ||||||
| class EmojiListWidget::Footer : public TabbedSelector::InnerFooter { | class EmojiListWidget::Footer : public TabbedSelector::InnerFooter { | ||||||
| public: | public: | ||||||
| 	Footer(not_null<EmojiListWidget*> parent); | 	Footer(not_null<EmojiListWidget*> parent); | ||||||
|  | @ -95,11 +146,9 @@ void EmojiListWidget::Footer::setActiveSection(Ui::Emoji::Section section) { | ||||||
| 	_pan->showEmojiSection(section); | 	_pan->showEmojiSection(section); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| EmojiColorPicker::EmojiColorPicker(QWidget *parent) : TWidget(parent) { | EmojiColorPicker::EmojiColorPicker(QWidget *parent) | ||||||
|  | : RpWidget(parent) { | ||||||
| 	setMouseTracking(true); | 	setMouseTracking(true); | ||||||
| 
 |  | ||||||
| 	_hideTimer.setSingleShot(true); |  | ||||||
| 	connect(&_hideTimer, SIGNAL(timeout()), this, SLOT(hideAnimated())); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void EmojiColorPicker::showEmoji(EmojiPtr emoji) { | void EmojiColorPicker::showEmoji(EmojiPtr emoji) { | ||||||
|  | @ -167,16 +216,6 @@ void EmojiColorPicker::paintEvent(QPaintEvent *e) { | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void EmojiColorPicker::enterEventHook(QEvent *e) { |  | ||||||
| 	_hideTimer.stop(); |  | ||||||
| 	if (_hiding) showAnimated(); |  | ||||||
| 	TWidget::enterEventHook(e); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void EmojiColorPicker::leaveEventHook(QEvent *e) { |  | ||||||
| 	TWidget::leaveEventHook(e); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void EmojiColorPicker::mousePressEvent(QMouseEvent *e) { | void EmojiColorPicker::mousePressEvent(QMouseEvent *e) { | ||||||
| 	if (e->button() != Qt::LeftButton) { | 	if (e->button() != Qt::LeftButton) { | ||||||
| 		return; | 		return; | ||||||
|  | @ -197,7 +236,7 @@ void EmojiColorPicker::handleMouseRelease(QPoint globalPos) { | ||||||
| 
 | 
 | ||||||
| 	updateSelected(); | 	updateSelected(); | ||||||
| 	if (_selected >= 0 && (pressed < 0 || _selected == pressed)) { | 	if (_selected >= 0 && (pressed < 0 || _selected == pressed)) { | ||||||
| 		emit emojiSelected(_variants[_selected]); | 		_chosen.fire_copy(_variants[_selected]); | ||||||
| 	} | 	} | ||||||
| 	_ignoreShow = true; | 	_ignoreShow = true; | ||||||
| 	hideAnimated(); | 	hideAnimated(); | ||||||
|  | @ -223,7 +262,7 @@ void EmojiColorPicker::animationCallback() { | ||||||
| 		_cache = QPixmap(); | 		_cache = QPixmap(); | ||||||
| 		if (_hiding) { | 		if (_hiding) { | ||||||
| 			hide(); | 			hide(); | ||||||
| 			emit hidden(); | 			_hidden.fire({}); | ||||||
| 		} else { | 		} else { | ||||||
| 			_lastMousePos = QCursor::pos(); | 			_lastMousePos = QCursor::pos(); | ||||||
| 			updateSelected(); | 			updateSelected(); | ||||||
|  | @ -236,7 +275,15 @@ void EmojiColorPicker::hideFast() { | ||||||
| 	_a_opacity.finish(); | 	_a_opacity.finish(); | ||||||
| 	_cache = QPixmap(); | 	_cache = QPixmap(); | ||||||
| 	hide(); | 	hide(); | ||||||
| 	emit hidden(); | 	_hidden.fire({}); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | rpl::producer<EmojiPtr> EmojiColorPicker::chosen() const { | ||||||
|  | 	return _chosen.events(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | rpl::producer<> EmojiColorPicker::hidden() const { | ||||||
|  | 	return _hidden.events(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void EmojiColorPicker::hideAnimated() { | void EmojiColorPicker::hideAnimated() { | ||||||
|  | @ -333,7 +380,8 @@ EmojiListWidget::EmojiListWidget( | ||||||
| 	QWidget *parent, | 	QWidget *parent, | ||||||
| 	not_null<Window::Controller*> controller) | 	not_null<Window::Controller*> controller) | ||||||
| : Inner(parent, controller) | : Inner(parent, controller) | ||||||
| , _picker(this) { | , _picker(this) | ||||||
|  | , _showPickerTimer([=] { showPicker(); }) { | ||||||
| 	setMouseTracking(true); | 	setMouseTracking(true); | ||||||
| 	setAttribute(Qt::WA_OpaquePaintEvent); | 	setAttribute(Qt::WA_OpaquePaintEvent); | ||||||
| 
 | 
 | ||||||
|  | @ -345,10 +393,18 @@ EmojiListWidget::EmojiListWidget( | ||||||
| 		_counts[i] = Ui::Emoji::GetSectionCount(static_cast<Section>(i)); | 		_counts[i] = Ui::Emoji::GetSectionCount(static_cast<Section>(i)); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	_showPickerTimer.setSingleShot(true); | 	_picker->chosen( | ||||||
| 	connect(&_showPickerTimer, SIGNAL(timeout()), this, SLOT(onShowPicker())); | 	) | rpl::start_with_next([=](EmojiPtr emoji) { | ||||||
| 	connect(_picker, SIGNAL(emojiSelected(EmojiPtr)), this, SLOT(onColorSelected(EmojiPtr))); | 		colorChosen(emoji); | ||||||
| 	connect(_picker, SIGNAL(hidden()), this, SLOT(onPickerHidden())); | 	}, lifetime()); | ||||||
|  | 	_picker->hidden( | ||||||
|  | 	) | rpl::start_with_next([=] { | ||||||
|  | 		pickerHidden(); | ||||||
|  | 	}, lifetime()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | rpl::producer<EmojiPtr> EmojiListWidget::chosen() const { | ||||||
|  | 	return _chosen.events(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void EmojiListWidget::visibleTopBottomUpdated( | void EmojiListWidget::visibleTopBottomUpdated( | ||||||
|  | @ -529,9 +585,9 @@ void EmojiListWidget::mousePressEvent(QMouseEvent *e) { | ||||||
| 			_pickerSel = _selected; | 			_pickerSel = _selected; | ||||||
| 			setCursor(style::cur_default); | 			setCursor(style::cur_default); | ||||||
| 			if (!cEmojiVariants().contains(_emoji[section][sel]->nonColoredId())) { | 			if (!cEmojiVariants().contains(_emoji[section][sel]->nonColoredId())) { | ||||||
| 				onShowPicker(); | 				showPicker(); | ||||||
| 			} else { | 			} else { | ||||||
| 				_showPickerTimer.start(500); | 				_showPickerTimer.callOnce(500); | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  | @ -559,7 +615,7 @@ void EmojiListWidget::mouseReleaseEvent(QMouseEvent *e) { | ||||||
| 	updateSelected(); | 	updateSelected(); | ||||||
| 
 | 
 | ||||||
| 	if (_showPickerTimer.isActive()) { | 	if (_showPickerTimer.isActive()) { | ||||||
| 		_showPickerTimer.stop(); | 		_showPickerTimer.cancel(); | ||||||
| 		_pickerSel = -1; | 		_pickerSel = -1; | ||||||
| 		_picker->hide(); | 		_picker->hide(); | ||||||
| 	} | 	} | ||||||
|  | @ -582,10 +638,10 @@ void EmojiListWidget::mouseReleaseEvent(QMouseEvent *e) { | ||||||
| 
 | 
 | ||||||
| void EmojiListWidget::selectEmoji(EmojiPtr emoji) { | void EmojiListWidget::selectEmoji(EmojiPtr emoji) { | ||||||
| 	Ui::Emoji::AddRecent(emoji); | 	Ui::Emoji::AddRecent(emoji); | ||||||
| 	emit selected(emoji); | 	_chosen.fire_copy(emoji); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void EmojiListWidget::onShowPicker() { | void EmojiListWidget::showPicker() { | ||||||
| 	if (_pickerSel < 0) return; | 	if (_pickerSel < 0) return; | ||||||
| 
 | 
 | ||||||
| 	auto section = (_pickerSel / MatrixRowShift); | 	auto section = (_pickerSel / MatrixRowShift); | ||||||
|  | @ -607,7 +663,7 @@ void EmojiListWidget::onShowPicker() { | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void EmojiListWidget::onPickerHidden() { | void EmojiListWidget::pickerHidden() { | ||||||
| 	_pickerSel = -1; | 	_pickerSel = -1; | ||||||
| 	update(); | 	update(); | ||||||
| 	emit disableScroll(false); | 	emit disableScroll(false); | ||||||
|  | @ -627,7 +683,7 @@ QRect EmojiListWidget::emojiRect(int section, int sel) { | ||||||
| 	return QRect(x, y, _singleSize.width(), _singleSize.height()); | 	return QRect(x, y, _singleSize.width(), _singleSize.height()); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void EmojiListWidget::onColorSelected(EmojiPtr emoji) { | void EmojiListWidget::colorChosen(EmojiPtr emoji) { | ||||||
| 	if (emoji->hasVariants()) { | 	if (emoji->hasVariants()) { | ||||||
| 		cRefEmojiVariants().insert( | 		cRefEmojiVariants().insert( | ||||||
| 			emoji->nonColoredId(), | 			emoji->nonColoredId(), | ||||||
|  | @ -792,7 +848,7 @@ void EmojiListWidget::showEmojiSection(Section section) { | ||||||
| 		} | 		} | ||||||
| 		return true; | 		return true; | ||||||
| 	}); | 	}); | ||||||
| 	emit scrollToY(y); | 	scrollTo(y); | ||||||
| 
 | 
 | ||||||
| 	_lastMousePos = QCursor::pos(); | 	_lastMousePos = QCursor::pos(); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL | ||||||
| 
 | 
 | ||||||
| #include "chat_helpers/tabbed_selector.h" | #include "chat_helpers/tabbed_selector.h" | ||||||
| #include "ui/widgets/tooltip.h" | #include "ui/widgets/tooltip.h" | ||||||
|  | #include "base/timer.h" | ||||||
| 
 | 
 | ||||||
| namespace Ui { | namespace Ui { | ||||||
| namespace Emoji { | namespace Emoji { | ||||||
|  | @ -24,66 +25,11 @@ namespace ChatHelpers { | ||||||
| 
 | 
 | ||||||
| constexpr auto kEmojiSectionCount = 8; | constexpr auto kEmojiSectionCount = 8; | ||||||
| 
 | 
 | ||||||
| class EmojiColorPicker : public TWidget { | class EmojiColorPicker; | ||||||
| 	Q_OBJECT |  | ||||||
| 
 |  | ||||||
| public: |  | ||||||
| 	EmojiColorPicker(QWidget *parent); |  | ||||||
| 
 |  | ||||||
| 	void showEmoji(EmojiPtr emoji); |  | ||||||
| 
 |  | ||||||
| 	void clearSelection(); |  | ||||||
| 	void handleMouseMove(QPoint globalPos); |  | ||||||
| 	void handleMouseRelease(QPoint globalPos); |  | ||||||
| 	void setSingleSize(QSize size); |  | ||||||
| 
 |  | ||||||
| 	void hideFast(); |  | ||||||
| 
 |  | ||||||
| public slots: |  | ||||||
| 	void showAnimated(); |  | ||||||
| 	void hideAnimated(); |  | ||||||
| 
 |  | ||||||
| signals: |  | ||||||
| 	void emojiSelected(EmojiPtr emoji); |  | ||||||
| 	void hidden(); |  | ||||||
| 
 |  | ||||||
| protected: |  | ||||||
| 	void paintEvent(QPaintEvent *e) override; |  | ||||||
| 	void enterEventHook(QEvent *e) override; |  | ||||||
| 	void leaveEventHook(QEvent *e) override; |  | ||||||
| 	void mousePressEvent(QMouseEvent *e) override; |  | ||||||
| 	void mouseReleaseEvent(QMouseEvent *e) override; |  | ||||||
| 	void mouseMoveEvent(QMouseEvent *e) override; |  | ||||||
| 
 |  | ||||||
| private: |  | ||||||
| 	void animationCallback(); |  | ||||||
| 	void updateSize(); |  | ||||||
| 
 |  | ||||||
| 	void drawVariant(Painter &p, int variant); |  | ||||||
| 
 |  | ||||||
| 	void updateSelected(); |  | ||||||
| 	void setSelected(int newSelected); |  | ||||||
| 
 |  | ||||||
| 	bool _ignoreShow = false; |  | ||||||
| 
 |  | ||||||
| 	QVector<EmojiPtr> _variants; |  | ||||||
| 
 |  | ||||||
| 	int _selected = -1; |  | ||||||
| 	int _pressedSel = -1; |  | ||||||
| 	QPoint _lastMousePos; |  | ||||||
| 	QSize _singleSize; |  | ||||||
| 
 |  | ||||||
| 	bool _hiding = false; |  | ||||||
| 	QPixmap _cache; |  | ||||||
| 	Animation _a_opacity; |  | ||||||
| 
 |  | ||||||
| 	QTimer _hideTimer; |  | ||||||
| 
 |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| class EmojiListWidget : public TabbedSelector::Inner, public Ui::AbstractTooltipShower { |  | ||||||
| 	Q_OBJECT |  | ||||||
| 
 | 
 | ||||||
|  | class EmojiListWidget | ||||||
|  | 	: public TabbedSelector::Inner | ||||||
|  | 	, public Ui::AbstractTooltipShower { | ||||||
| public: | public: | ||||||
| 	EmojiListWidget(QWidget *parent, not_null<Window::Controller*> controller); | 	EmojiListWidget(QWidget *parent, not_null<Window::Controller*> controller); | ||||||
| 
 | 
 | ||||||
|  | @ -100,16 +46,7 @@ public: | ||||||
| 	QString tooltipText() const override; | 	QString tooltipText() const override; | ||||||
| 	QPoint tooltipPos() const override; | 	QPoint tooltipPos() const override; | ||||||
| 
 | 
 | ||||||
| public slots: | 	rpl::producer<EmojiPtr> chosen() const; | ||||||
| 	void onShowPicker(); |  | ||||||
| 	void onPickerHidden(); |  | ||||||
| 	void onColorSelected(EmojiPtr emoji); |  | ||||||
| 
 |  | ||||||
| 	bool checkPickerHide(); |  | ||||||
| 
 |  | ||||||
| signals: |  | ||||||
| 	void selected(EmojiPtr emoji); |  | ||||||
| 	void switchToStickers(); |  | ||||||
| 
 | 
 | ||||||
| protected: | protected: | ||||||
| 	void visibleTopBottomUpdated( | 	void visibleTopBottomUpdated( | ||||||
|  | @ -146,8 +83,12 @@ private: | ||||||
| 	SectionInfo sectionInfo(int section) const; | 	SectionInfo sectionInfo(int section) const; | ||||||
| 	SectionInfo sectionInfoByOffset(int yOffset) const; | 	SectionInfo sectionInfoByOffset(int yOffset) const; | ||||||
| 
 | 
 | ||||||
|  | 	void showPicker(); | ||||||
|  | 	void pickerHidden(); | ||||||
|  | 	void colorChosen(EmojiPtr emoji); | ||||||
|  | 	bool checkPickerHide(); | ||||||
|  | 
 | ||||||
| 	void ensureLoaded(int section); | 	void ensureLoaded(int section); | ||||||
| 	int countSectionTop(int section) const; |  | ||||||
| 	void updateSelected(); | 	void updateSelected(); | ||||||
| 	void setSelected(int newSelected); | 	void setSelected(int newSelected); | ||||||
| 
 | 
 | ||||||
|  | @ -171,7 +112,9 @@ private: | ||||||
| 	QPoint _lastMousePos; | 	QPoint _lastMousePos; | ||||||
| 
 | 
 | ||||||
| 	object_ptr<EmojiColorPicker> _picker; | 	object_ptr<EmojiColorPicker> _picker; | ||||||
| 	QTimer _showPickerTimer; | 	base::Timer _showPickerTimer; | ||||||
|  | 
 | ||||||
|  | 	rpl::event_stream<EmojiPtr> _chosen; | ||||||
| 
 | 
 | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -513,15 +513,7 @@ FieldAutocompleteInner::FieldAutocompleteInner(FieldAutocomplete *parent, Mentio | ||||||
| , _hrows(hrows) | , _hrows(hrows) | ||||||
| , _brows(brows) | , _brows(brows) | ||||||
| , _srows(srows) | , _srows(srows) | ||||||
| , _stickersPerRow(1) | , _previewTimer([=] { showPreview(); }) { | ||||||
| , _recentInlineBotsInRows(0) |  | ||||||
| , _sel(-1) |  | ||||||
| , _down(-1) |  | ||||||
| , _mouseSel(false) |  | ||||||
| , _overDelete(false) |  | ||||||
| , _previewShown(false) { |  | ||||||
| 	_previewTimer.setSingleShot(true); |  | ||||||
| 	connect(&_previewTimer, SIGNAL(timeout()), this, SLOT(onPreview())); |  | ||||||
| 	subscribe(Auth().downloaderTaskFinished(), [this] { update(); }); | 	subscribe(Auth().downloaderTaskFinished(), [this] { update(); }); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -806,13 +798,13 @@ void FieldAutocompleteInner::mousePressEvent(QMouseEvent *e) { | ||||||
| 			chooseSelected(FieldAutocomplete::ChooseMethod::ByClick); | 			chooseSelected(FieldAutocomplete::ChooseMethod::ByClick); | ||||||
| 		} else { | 		} else { | ||||||
| 			_down = _sel; | 			_down = _sel; | ||||||
| 			_previewTimer.start(QApplication::startDragTime()); | 			_previewTimer.callOnce(QApplication::startDragTime()); | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void FieldAutocompleteInner::mouseReleaseEvent(QMouseEvent *e) { | void FieldAutocompleteInner::mouseReleaseEvent(QMouseEvent *e) { | ||||||
| 	_previewTimer.stop(); | 	_previewTimer.cancel(); | ||||||
| 
 | 
 | ||||||
| 	int32 pressed = _down; | 	int32 pressed = _down; | ||||||
| 	_down = -1; | 	_down = -1; | ||||||
|  | @ -915,7 +907,7 @@ void FieldAutocompleteInner::onParentGeometryChanged() { | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void FieldAutocompleteInner::onPreview() { | void FieldAutocompleteInner::showPreview() { | ||||||
| 	if (_down >= 0 && _down < _srows->size()) { | 	if (_down >= 0 && _down < _srows->size()) { | ||||||
| 		Ui::showMediaPreview( | 		Ui::showMediaPreview( | ||||||
| 			(*_srows)[_down]->stickerSetOrigin(), | 			(*_srows)[_down]->stickerSetOrigin(), | ||||||
|  |  | ||||||
|  | @ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| #include "ui/twidget.h" | #include "ui/twidget.h" | ||||||
|  | #include "base/timer.h" | ||||||
| #include "chat_helpers/stickers.h" | #include "chat_helpers/stickers.h" | ||||||
| 
 | 
 | ||||||
| namespace Ui { | namespace Ui { | ||||||
|  | @ -152,7 +153,6 @@ signals: | ||||||
| public slots: | public slots: | ||||||
| 	void onParentGeometryChanged(); | 	void onParentGeometryChanged(); | ||||||
| 	void onUpdateSelected(bool force = false); | 	void onUpdateSelected(bool force = false); | ||||||
| 	void onPreview(); |  | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
| 	void paintEvent(QPaintEvent *e) override; | 	void paintEvent(QPaintEvent *e) override; | ||||||
|  | @ -167,22 +167,25 @@ private: | ||||||
| 
 | 
 | ||||||
| 	void updateSelectedRow(); | 	void updateSelectedRow(); | ||||||
| 	void setSel(int sel, bool scroll = false); | 	void setSel(int sel, bool scroll = false); | ||||||
|  | 	void showPreview(); | ||||||
| 
 | 
 | ||||||
| 	FieldAutocomplete *_parent; | 	FieldAutocomplete *_parent = nullptr; | ||||||
| 	MentionRows *_mrows; | 	MentionRows *_mrows = nullptr; | ||||||
| 	HashtagRows *_hrows; | 	HashtagRows *_hrows = nullptr; | ||||||
| 	BotCommandRows *_brows; | 	BotCommandRows *_brows = nullptr; | ||||||
| 	StickerRows *_srows; | 	StickerRows *_srows = nullptr; | ||||||
| 	int32 _stickersPerRow, _recentInlineBotsInRows; | 	int _stickersPerRow = 1; | ||||||
| 	int32 _sel, _down; | 	int _recentInlineBotsInRows = 0; | ||||||
| 	bool _mouseSel; | 	int _sel = -1; | ||||||
|  | 	int _down = -1; | ||||||
|  | 	bool _mouseSel = false; | ||||||
| 	QPoint _mousePos; | 	QPoint _mousePos; | ||||||
| 
 | 
 | ||||||
| 	bool _overDelete; | 	bool _overDelete = false; | ||||||
| 
 | 
 | ||||||
| 	bool _previewShown; | 	bool _previewShown = false; | ||||||
| 
 | 
 | ||||||
| 	QTimer _previewTimer; | 	base::Timer _previewTimer; | ||||||
| 
 | 
 | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -69,7 +69,7 @@ GifsListWidget::Footer::Footer(not_null<GifsListWidget*> parent) : InnerFooter(p | ||||||
| 	}); | 	}); | ||||||
| 	connect(_field, &Ui::InputField::cancelled, [=] { | 	connect(_field, &Ui::InputField::cancelled, [=] { | ||||||
| 		if (_field->getLastText().isEmpty()) { | 		if (_field->getLastText().isEmpty()) { | ||||||
| 			emit _pan->cancelled(); | 			_pan->cancelled(); | ||||||
| 		} else { | 		} else { | ||||||
| 			_field->setText(QString()); | 			_field->setText(QString()); | ||||||
| 		} | 		} | ||||||
|  | @ -125,16 +125,12 @@ GifsListWidget::GifsListWidget( | ||||||
| 	QWidget *parent, | 	QWidget *parent, | ||||||
| 	not_null<Window::Controller*> controller) | 	not_null<Window::Controller*> controller) | ||||||
| : Inner(parent, controller) | : Inner(parent, controller) | ||||||
| , _section(Section::Gifs) { | , _section(Section::Gifs) | ||||||
|  | , _updateInlineItems([=] { updateInlineItems(); }) | ||||||
|  | , _previewTimer([=] { showPreview(); }) { | ||||||
| 	setMouseTracking(true); | 	setMouseTracking(true); | ||||||
| 	setAttribute(Qt::WA_OpaquePaintEvent); | 	setAttribute(Qt::WA_OpaquePaintEvent); | ||||||
| 
 | 
 | ||||||
| 	_previewTimer.setSingleShot(true); |  | ||||||
| 	connect(&_previewTimer, SIGNAL(timeout()), this, SLOT(onPreview())); |  | ||||||
| 
 |  | ||||||
| 	_updateInlineItems.setSingleShot(true); |  | ||||||
| 	connect(&_updateInlineItems, SIGNAL(timeout()), this, SLOT(onUpdateInlineItems())); |  | ||||||
| 
 |  | ||||||
| 	_inlineRequestTimer.setSingleShot(true); | 	_inlineRequestTimer.setSingleShot(true); | ||||||
| 	connect(&_inlineRequestTimer, &QTimer::timeout, this, [this] { sendInlineRequest(); }); | 	connect(&_inlineRequestTimer, &QTimer::timeout, this, [this] { sendInlineRequest(); }); | ||||||
| 
 | 
 | ||||||
|  | @ -152,8 +148,22 @@ GifsListWidget::GifsListWidget( | ||||||
| 	}); | 	}); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | rpl::producer<not_null<DocumentData*>> GifsListWidget::fileChosen() const { | ||||||
|  | 	return _fileChosen.events(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | rpl::producer<not_null<PhotoData*>> GifsListWidget::photoChosen() const { | ||||||
|  | 	return _photoChosen.events(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | auto GifsListWidget::inlineResultChosen() const | ||||||
|  | -> rpl::producer<InlineChosen> { | ||||||
|  | 	return _inlineResultChosen.events(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| object_ptr<TabbedSelector::InnerFooter> GifsListWidget::createFooter() { | object_ptr<TabbedSelector::InnerFooter> GifsListWidget::createFooter() { | ||||||
| 	Expects(_footer == nullptr); | 	Expects(_footer == nullptr); | ||||||
|  | 
 | ||||||
| 	auto result = object_ptr<Footer>(this); | 	auto result = object_ptr<Footer>(this); | ||||||
| 	_footer = result; | 	_footer = result; | ||||||
| 	return std::move(result); | 	return std::move(result); | ||||||
|  | @ -305,11 +315,11 @@ void GifsListWidget::mousePressEvent(QMouseEvent *e) { | ||||||
| 
 | 
 | ||||||
| 	_pressed = _selected; | 	_pressed = _selected; | ||||||
| 	ClickHandler::pressed(); | 	ClickHandler::pressed(); | ||||||
| 	_previewTimer.start(QApplication::startDragTime()); | 	_previewTimer.callOnce(QApplication::startDragTime()); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void GifsListWidget::mouseReleaseEvent(QMouseEvent *e) { | void GifsListWidget::mouseReleaseEvent(QMouseEvent *e) { | ||||||
| 	_previewTimer.stop(); | 	_previewTimer.cancel(); | ||||||
| 
 | 
 | ||||||
| 	auto pressed = std::exchange(_pressed, -1); | 	auto pressed = std::exchange(_pressed, -1); | ||||||
| 	auto activated = ClickHandler::unpressed(); | 	auto activated = ClickHandler::unpressed(); | ||||||
|  | @ -340,29 +350,28 @@ void GifsListWidget::selectInlineResult(int row, int column) { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	auto item = _rows[row].items[column]; | 	auto item = _rows[row].items[column]; | ||||||
| 	if (auto photo = item->getPhoto()) { | 	if (const auto photo = item->getPhoto()) { | ||||||
| 		if (photo->medium->loaded() || photo->thumb->loaded()) { | 		if (photo->medium->loaded() || photo->thumb->loaded()) { | ||||||
| 			emit selected(photo); | 			_photoChosen.fire_copy(photo); | ||||||
| 		} else if (!photo->medium->loading()) { | 		} else if (!photo->medium->loading()) { | ||||||
| 			photo->thumb->loadEvenCancelled(Data::FileOrigin()); | 			photo->thumb->loadEvenCancelled(Data::FileOrigin()); | ||||||
| 			photo->medium->loadEvenCancelled(Data::FileOrigin()); | 			photo->medium->loadEvenCancelled(Data::FileOrigin()); | ||||||
| 		} | 		} | ||||||
| 	} else if (const auto document = item->getDocument()) { | 	} else if (const auto document = item->getDocument()) { | ||||||
| 		if (document->loaded()) { | 		if (document->loaded()) { | ||||||
| 			emit selected(document); | 			_fileChosen.fire_copy(document); | ||||||
| 		} else if (document->loading()) { | 		} else if (document->loading()) { | ||||||
| 			document->cancel(); | 			document->cancel(); | ||||||
| 		} else { | 		} else { | ||||||
| 
 |  | ||||||
| 			DocumentOpenClickHandler::Open( | 			DocumentOpenClickHandler::Open( | ||||||
| 				document->stickerOrGifOrigin(), | 				document->stickerOrGifOrigin(), | ||||||
| 				document, | 				document, | ||||||
| 				nullptr, | 				nullptr, | ||||||
| 				ActionOnLoadNone); | 				ActionOnLoadNone); | ||||||
| 		} | 		} | ||||||
| 	} else if (auto inlineResult = item->getResult()) { | 	} else if (const auto inlineResult = item->getResult()) { | ||||||
| 		if (inlineResult->onChoose(item)) { | 		if (inlineResult->onChoose(item)) { | ||||||
| 			emit selected(inlineResult, _searchBot); | 			_inlineResultChosen.fire({ inlineResult, _searchBot }); | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  | @ -622,8 +631,7 @@ void GifsListWidget::switchToSavedGifs() { | ||||||
| 	clearInlineRows(false); | 	clearInlineRows(false); | ||||||
| 	_section = Section::Gifs; | 	_section = Section::Gifs; | ||||||
| 	refreshSavedGifs(); | 	refreshSavedGifs(); | ||||||
| 	emit scrollToY(0); | 	scrollTo(0); | ||||||
| 	emit scrollUpdated(); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| int GifsListWidget::refreshInlineRows(const InlineCacheEntry *entry, bool resultsDeleted) { | int GifsListWidget::refreshInlineRows(const InlineCacheEntry *entry, bool resultsDeleted) { | ||||||
|  | @ -747,7 +755,7 @@ void GifsListWidget::inlineItemRepaint(const InlineBots::Layout::ItemBase *layou | ||||||
| 	if (_lastScrolled + 100 <= ms) { | 	if (_lastScrolled + 100 <= ms) { | ||||||
| 		update(); | 		update(); | ||||||
| 	} else { | 	} else { | ||||||
| 		_updateInlineItems.start(_lastScrolled + 100 - ms); | 		_updateInlineItems.callOnce(_lastScrolled + 100 - ms); | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -804,7 +812,7 @@ int32 GifsListWidget::showInlineRows(bool newResults) { | ||||||
| 	auto added = 0; | 	auto added = 0; | ||||||
| 	auto clear = !refreshInlineRows(&added); | 	auto clear = !refreshInlineRows(&added); | ||||||
| 	if (newResults) { | 	if (newResults) { | ||||||
| 		scrollToY(0); | 		scrollTo(0); | ||||||
| 	} | 	} | ||||||
| 	return added; | 	return added; | ||||||
| } | } | ||||||
|  | @ -847,6 +855,14 @@ void GifsListWidget::searchForGifs(const QString &query) { | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void GifsListWidget::cancelled() { | ||||||
|  | 	_cancelled.fire({}); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | rpl::producer<> GifsListWidget::cancelRequests() const { | ||||||
|  | 	return _cancelled.events(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void GifsListWidget::sendInlineRequest() { | void GifsListWidget::sendInlineRequest() { | ||||||
| 	if (_inlineRequestId || !_inlineQueryPeer || _inlineNextQuery.isEmpty()) { | 	if (_inlineRequestId || !_inlineQueryPeer || _inlineNextQuery.isEmpty()) { | ||||||
| 		return; | 		return; | ||||||
|  | @ -977,8 +993,10 @@ void GifsListWidget::updateSelected() { | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void GifsListWidget::onPreview() { | void GifsListWidget::showPreview() { | ||||||
| 	if (_pressed < 0) return; | 	if (_pressed < 0) { | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
| 	int row = _pressed / MatrixRowShift, col = _pressed % MatrixRowShift; | 	int row = _pressed / MatrixRowShift, col = _pressed % MatrixRowShift; | ||||||
| 	if (row < _rows.size() && col < _rows[row].items.size()) { | 	if (row < _rows.size() && col < _rows[row].items.size()) { | ||||||
| 		auto layout = _rows[row].items[col]; | 		auto layout = _rows[row].items[col]; | ||||||
|  | @ -996,12 +1014,12 @@ void GifsListWidget::onPreview() { | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void GifsListWidget::onUpdateInlineItems() { | void GifsListWidget::updateInlineItems() { | ||||||
| 	auto ms = getms(); | 	auto ms = getms(); | ||||||
| 	if (_lastScrolled + 100 <= ms) { | 	if (_lastScrolled + 100 <= ms) { | ||||||
| 		update(); | 		update(); | ||||||
| 	} else { | 	} else { | ||||||
| 		_updateInlineItems.start(_lastScrolled + 100 - ms); | 		_updateInlineItems.callOnce(_lastScrolled + 100 - ms); | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| #include "chat_helpers/tabbed_selector.h" | #include "chat_helpers/tabbed_selector.h" | ||||||
|  | #include "base/timer.h" | ||||||
| #include "inline_bots/inline_bot_layout_item.h" | #include "inline_bots/inline_bot_layout_item.h" | ||||||
| 
 | 
 | ||||||
| namespace InlineBots { | namespace InlineBots { | ||||||
|  | @ -32,11 +33,15 @@ class GifsListWidget | ||||||
| 	, public InlineBots::Layout::Context | 	, public InlineBots::Layout::Context | ||||||
| 	, private base::Subscriber | 	, private base::Subscriber | ||||||
| 	, private MTP::Sender { | 	, private MTP::Sender { | ||||||
| 	Q_OBJECT |  | ||||||
| 
 |  | ||||||
| public: | public: | ||||||
|  | 	using InlineChosen = TabbedSelector::InlineChosen; | ||||||
|  | 
 | ||||||
| 	GifsListWidget(QWidget *parent, not_null<Window::Controller*> controller); | 	GifsListWidget(QWidget *parent, not_null<Window::Controller*> controller); | ||||||
| 
 | 
 | ||||||
|  | 	rpl::producer<not_null<DocumentData*>> fileChosen() const; | ||||||
|  | 	rpl::producer<not_null<PhotoData*>> photoChosen() const; | ||||||
|  | 	rpl::producer<InlineChosen> inlineResultChosen() const; | ||||||
|  | 
 | ||||||
| 	void refreshRecent() override; | 	void refreshRecent() override; | ||||||
| 	void preloadImages() override; | 	void preloadImages() override; | ||||||
| 	void clearSelection() override; | 	void clearSelection() override; | ||||||
|  | @ -56,6 +61,9 @@ public: | ||||||
| 	void searchForGifs(const QString &query); | 	void searchForGifs(const QString &query); | ||||||
| 	void sendInlineRequest(); | 	void sendInlineRequest(); | ||||||
| 
 | 
 | ||||||
|  | 	void cancelled(); | ||||||
|  | 	rpl::producer<> cancelRequests() const; | ||||||
|  | 
 | ||||||
| 	~GifsListWidget(); | 	~GifsListWidget(); | ||||||
| 
 | 
 | ||||||
| protected: | protected: | ||||||
|  | @ -76,21 +84,6 @@ protected: | ||||||
| 	void processPanelHideFinished() override; | 	void processPanelHideFinished() override; | ||||||
| 	int countDesiredHeight(int newWidth) override; | 	int countDesiredHeight(int newWidth) override; | ||||||
| 
 | 
 | ||||||
| private slots: |  | ||||||
| 	void onPreview(); |  | ||||||
| 	void onUpdateInlineItems(); |  | ||||||
| 
 |  | ||||||
| signals: |  | ||||||
| 	void selected(not_null<DocumentData*> sticker); |  | ||||||
| 	void selected(not_null<PhotoData*> photo); |  | ||||||
| 	void selected( |  | ||||||
| 		not_null<InlineBots::Result*> result, |  | ||||||
| 		not_null<UserData*> bot); |  | ||||||
| 	void cancelled(); |  | ||||||
| 
 |  | ||||||
| 	void emptyInlineRows(); |  | ||||||
| 	void scrollUpdated(); |  | ||||||
| 
 |  | ||||||
| private: | private: | ||||||
| 	enum class Section { | 	enum class Section { | ||||||
| 		Inlines, | 		Inlines, | ||||||
|  | @ -120,9 +113,12 @@ private: | ||||||
| 	void updateSelected(); | 	void updateSelected(); | ||||||
| 	void paintInlineItems(Painter &p, QRect clip); | 	void paintInlineItems(Painter &p, QRect clip); | ||||||
| 
 | 
 | ||||||
|  | 	void updateInlineItems(); | ||||||
|  | 	void showPreview(); | ||||||
|  | 
 | ||||||
| 	Section _section = Section::Gifs; | 	Section _section = Section::Gifs; | ||||||
| 	TimeMs _lastScrolled = 0; | 	TimeMs _lastScrolled = 0; | ||||||
| 	QTimer _updateInlineItems; | 	base::Timer _updateInlineItems; | ||||||
| 	bool _inlineWithThumb = false; | 	bool _inlineWithThumb = false; | ||||||
| 
 | 
 | ||||||
| 	struct Row { | 	struct Row { | ||||||
|  | @ -156,7 +152,7 @@ private: | ||||||
| 	int _pressed = -1; | 	int _pressed = -1; | ||||||
| 	QPoint _lastMousePos; | 	QPoint _lastMousePos; | ||||||
| 
 | 
 | ||||||
| 	QTimer _previewTimer; | 	base::Timer _previewTimer; | ||||||
| 	bool _previewShown = false; | 	bool _previewShown = false; | ||||||
| 
 | 
 | ||||||
| 	std::map<QString, std::unique_ptr<InlineCacheEntry>> _inlineCache; | 	std::map<QString, std::unique_ptr<InlineCacheEntry>> _inlineCache; | ||||||
|  | @ -168,6 +164,11 @@ private: | ||||||
| 	QString _inlineQuery, _inlineNextQuery, _inlineNextOffset; | 	QString _inlineQuery, _inlineNextQuery, _inlineNextOffset; | ||||||
| 	mtpRequestId _inlineRequestId = 0; | 	mtpRequestId _inlineRequestId = 0; | ||||||
| 
 | 
 | ||||||
|  | 	rpl::event_stream<not_null<DocumentData*>> _fileChosen; | ||||||
|  | 	rpl::event_stream<not_null<PhotoData*>> _photoChosen; | ||||||
|  | 	rpl::event_stream<InlineChosen> _inlineResultChosen; | ||||||
|  | 	rpl::event_stream<> _cancelled; | ||||||
|  | 
 | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| } // namespace ChatHelpers
 | } // namespace ChatHelpers
 | ||||||
|  |  | ||||||
|  | @ -682,14 +682,14 @@ StickersListWidget::StickersListWidget(QWidget *parent, not_null<Window::Control | ||||||
| , _addText(lang(lng_stickers_featured_add).toUpper()) | , _addText(lang(lng_stickers_featured_add).toUpper()) | ||||||
| , _addWidth(st::stickersTrendingAdd.font->width(_addText)) | , _addWidth(st::stickersTrendingAdd.font->width(_addText)) | ||||||
| , _settings(this, lang(lng_stickers_you_have)) | , _settings(this, lang(lng_stickers_you_have)) | ||||||
|  | , _previewTimer([=] { showPreview(); }) | ||||||
| , _searchRequestTimer([=] { sendSearchRequest(); }) { | , _searchRequestTimer([=] { sendSearchRequest(); }) { | ||||||
| 	setMouseTracking(true); | 	setMouseTracking(true); | ||||||
| 	setAttribute(Qt::WA_OpaquePaintEvent); | 	setAttribute(Qt::WA_OpaquePaintEvent); | ||||||
| 
 | 
 | ||||||
| 	connect(_settings, SIGNAL(clicked()), this, SLOT(onSettings())); | 	_settings->addClickHandler([] { | ||||||
| 
 | 		Ui::show(Box<StickersBox>(StickersBox::Section::Installed)); | ||||||
| 	_previewTimer.setSingleShot(true); | 	}); | ||||||
| 	connect(&_previewTimer, SIGNAL(timeout()), this, SLOT(onPreview())); |  | ||||||
| 
 | 
 | ||||||
| 	subscribe(Auth().downloaderTaskFinished(), [=] { | 	subscribe(Auth().downloaderTaskFinished(), [=] { | ||||||
| 		if (isVisible()) { | 		if (isVisible()) { | ||||||
|  | @ -704,8 +704,21 @@ StickersListWidget::StickersListWidget(QWidget *parent, not_null<Window::Control | ||||||
| 	})); | 	})); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | rpl::producer<not_null<DocumentData*>> StickersListWidget::chosen() const { | ||||||
|  | 	return _chosen.events(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | rpl::producer<> StickersListWidget::scrollUpdated() const { | ||||||
|  | 	return _scrollUpdated.events(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | rpl::producer<> StickersListWidget::checkForHide() const { | ||||||
|  | 	return _checkForHide.events(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| object_ptr<TabbedSelector::InnerFooter> StickersListWidget::createFooter() { | object_ptr<TabbedSelector::InnerFooter> StickersListWidget::createFooter() { | ||||||
| 	Expects(_footer == nullptr); | 	Expects(_footer == nullptr); | ||||||
|  | 
 | ||||||
| 	auto result = object_ptr<Footer>(this); | 	auto result = object_ptr<Footer>(this); | ||||||
| 	_footer = result; | 	_footer = result; | ||||||
| 	return std::move(result); | 	return std::move(result); | ||||||
|  | @ -927,7 +940,7 @@ void StickersListWidget::cancelSetsSearch() { | ||||||
| 
 | 
 | ||||||
| void StickersListWidget::showSearchResults() { | void StickersListWidget::showSearchResults() { | ||||||
| 	refreshSearchRows(); | 	refreshSearchRows(); | ||||||
| 	scrollToY(0); | 	scrollTo(0); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void StickersListWidget::refreshSearchRows() { | void StickersListWidget::refreshSearchRows() { | ||||||
|  | @ -1418,7 +1431,7 @@ void StickersListWidget::mousePressEvent(QMouseEvent *e) { | ||||||
| 
 | 
 | ||||||
| 	setPressed(_selected); | 	setPressed(_selected); | ||||||
| 	ClickHandler::pressed(); | 	ClickHandler::pressed(); | ||||||
| 	_previewTimer.start(QApplication::startDragTime()); | 	_previewTimer.callOnce(QApplication::startDragTime()); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void StickersListWidget::setPressed(OverState newPressed) { | void StickersListWidget::setPressed(OverState newPressed) { | ||||||
|  | @ -1498,7 +1511,7 @@ QPoint StickersListWidget::buttonRippleTopLeft(int section) const { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void StickersListWidget::mouseReleaseEvent(QMouseEvent *e) { | void StickersListWidget::mouseReleaseEvent(QMouseEvent *e) { | ||||||
| 	_previewTimer.stop(); | 	_previewTimer.cancel(); | ||||||
| 
 | 
 | ||||||
| 	auto pressed = _pressed; | 	auto pressed = _pressed; | ||||||
| 	setPressed(std::nullopt); | 	setPressed(std::nullopt); | ||||||
|  | @ -1531,7 +1544,7 @@ void StickersListWidget::mouseReleaseEvent(QMouseEvent *e) { | ||||||
| 				} | 				} | ||||||
| 				return; | 				return; | ||||||
| 			} | 			} | ||||||
| 			emit selected(set.pack[sticker->index]); | 			_chosen.fire_copy(set.pack[sticker->index]); | ||||||
| 		} else if (auto set = base::get_if<OverSet>(&pressed)) { | 		} else if (auto set = base::get_if<OverSet>(&pressed)) { | ||||||
| 			Assert(set->section >= 0 && set->section < sets.size()); | 			Assert(set->section >= 0 && set->section < sets.size()); | ||||||
| 			displaySet(sets[set->section].id); | 			displaySet(sets[set->section].id); | ||||||
|  | @ -2170,15 +2183,11 @@ void StickersListWidget::setSelected(OverState newSelected) { | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void StickersListWidget::onSettings() { | void StickersListWidget::showPreview() { | ||||||
| 	Ui::show(Box<StickersBox>(StickersBox::Section::Installed)); | 	if (const auto sticker = base::get_if<OverSticker>(&_pressed)) { | ||||||
| } | 		const auto &sets = shownSets(); | ||||||
| 
 |  | ||||||
| void StickersListWidget::onPreview() { |  | ||||||
| 	if (auto sticker = base::get_if<OverSticker>(&_pressed)) { |  | ||||||
| 		auto &sets = shownSets(); |  | ||||||
| 		Assert(sticker->section >= 0 && sticker->section < sets.size()); | 		Assert(sticker->section >= 0 && sticker->section < sets.size()); | ||||||
| 		auto &set = sets[sticker->section]; | 		const auto &set = sets[sticker->section]; | ||||||
| 		Assert(sticker->index >= 0 && sticker->index < set.pack.size()); | 		Assert(sticker->index >= 0 && sticker->index < set.pack.size()); | ||||||
| 		Ui::showMediaPreview( | 		Ui::showMediaPreview( | ||||||
| 			set.pack[sticker->index]->stickerSetOrigin(), | 			set.pack[sticker->index]->stickerSetOrigin(), | ||||||
|  | @ -2202,8 +2211,8 @@ void StickersListWidget::showStickerSet(uint64 setId) { | ||||||
| 			update(); | 			update(); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		emit scrollToY(0); | 		scrollTo(0); | ||||||
| 		emit scrollUpdated(); | 		_scrollUpdated.fire({}); | ||||||
| 		return; | 		return; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | @ -2222,8 +2231,8 @@ void StickersListWidget::showStickerSet(uint64 setId) { | ||||||
| 		} | 		} | ||||||
| 		return true; | 		return true; | ||||||
| 	}); | 	}); | ||||||
| 	emit scrollToY(y); | 	scrollTo(y); | ||||||
| 	emit scrollUpdated(); | 	_scrollUpdated.fire({}); | ||||||
| 
 | 
 | ||||||
| 	if (needRefresh && _footer) { | 	if (needRefresh && _footer) { | ||||||
| 		_footer->refreshIcons(ValidateIconAnimations::Scroll); | 		_footer->refreshIcons(ValidateIconAnimations::Scroll); | ||||||
|  | @ -2281,7 +2290,7 @@ void StickersListWidget::displaySet(uint64 setId) { | ||||||
| 			auto box = Ui::show(Box<StickersBox>(_megagroupSet)); | 			auto box = Ui::show(Box<StickersBox>(_megagroupSet)); | ||||||
| 			connect(box, &QObject::destroyed, this, [this] { | 			connect(box, &QObject::destroyed, this, [this] { | ||||||
| 				_displayingSetId = 0; | 				_displayingSetId = 0; | ||||||
| 				emit checkForHide(); | 				_checkForHide.fire({}); | ||||||
| 			}); | 			}); | ||||||
| 			return; | 			return; | ||||||
| 		} else if (_megagroupSet->mgInfo->stickerSet.type() == mtpc_inputStickerSetID) { | 		} else if (_megagroupSet->mgInfo->stickerSet.type() == mtpc_inputStickerSetID) { | ||||||
|  | @ -2299,7 +2308,7 @@ void StickersListWidget::displaySet(uint64 setId) { | ||||||
| 			LayerOption::KeepOther); | 			LayerOption::KeepOther); | ||||||
| 		connect(box, &QObject::destroyed, this, [this] { | 		connect(box, &QObject::destroyed, this, [this] { | ||||||
| 			_displayingSetId = 0; | 			_displayingSetId = 0; | ||||||
| 			emit checkForHide(); | 			_checkForHide.fire({}); | ||||||
| 		}); | 		}); | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  | @ -2357,10 +2366,10 @@ void StickersListWidget::removeMegagroupSet(bool locally) { | ||||||
| 		} | 		} | ||||||
| 		Ui::hideLayer(); | 		Ui::hideLayer(); | ||||||
| 		_removingSetId = 0; | 		_removingSetId = 0; | ||||||
| 		emit checkForHide(); | 		_checkForHide.fire({}); | ||||||
| 	}), crl::guard(this, [this] { | 	}), crl::guard(this, [this] { | ||||||
| 		_removingSetId = 0; | 		_removingSetId = 0; | ||||||
| 		emit checkForHide(); | 		_checkForHide.fire({}); | ||||||
| 	}))); | 	}))); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -2370,7 +2379,7 @@ void StickersListWidget::removeSet(uint64 setId) { | ||||||
| 	if (it != sets.cend()) { | 	if (it != sets.cend()) { | ||||||
| 		_removingSetId = it->id; | 		_removingSetId = it->id; | ||||||
| 		auto text = lng_stickers_remove_pack(lt_sticker_pack, it->title); | 		auto text = lng_stickers_remove_pack(lt_sticker_pack, it->title); | ||||||
| 		Ui::show(Box<ConfirmBox>(text, lang(lng_stickers_remove_pack_confirm), crl::guard(this, [this] { | 		Ui::show(Box<ConfirmBox>(text, lang(lng_stickers_remove_pack_confirm), crl::guard(this, [=] { | ||||||
| 			Ui::hideLayer(); | 			Ui::hideLayer(); | ||||||
| 			auto &sets = Auth().data().stickerSetsRef(); | 			auto &sets = Auth().data().stickerSetsRef(); | ||||||
| 			auto it = sets.find(_removingSetId); | 			auto it = sets.find(_removingSetId); | ||||||
|  | @ -2406,10 +2415,10 @@ void StickersListWidget::removeSet(uint64 setId) { | ||||||
| 				if (writeRecent) Local::writeUserSettings(); | 				if (writeRecent) Local::writeUserSettings(); | ||||||
| 			} | 			} | ||||||
| 			_removingSetId = 0; | 			_removingSetId = 0; | ||||||
| 			emit checkForHide(); | 			_checkForHide.fire({}); | ||||||
| 		}), crl::guard(this, [this] { | 		}), crl::guard(this, [=] { | ||||||
| 			_removingSetId = 0; | 			_removingSetId = 0; | ||||||
| 			emit checkForHide(); | 			_checkForHide.fire({}); | ||||||
| 		}))); | 		}))); | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -29,13 +29,15 @@ class StickersListWidget | ||||||
| 	: public TabbedSelector::Inner | 	: public TabbedSelector::Inner | ||||||
| 	, private base::Subscriber | 	, private base::Subscriber | ||||||
| 	, private MTP::Sender { | 	, private MTP::Sender { | ||||||
| 	Q_OBJECT |  | ||||||
| 
 |  | ||||||
| public: | public: | ||||||
| 	StickersListWidget( | 	StickersListWidget( | ||||||
| 		QWidget *parent, | 		QWidget *parent, | ||||||
| 		not_null<Window::Controller*> controller); | 		not_null<Window::Controller*> controller); | ||||||
| 
 | 
 | ||||||
|  | 	rpl::producer<not_null<DocumentData*>> chosen() const; | ||||||
|  | 	rpl::producer<> scrollUpdated() const; | ||||||
|  | 	rpl::producer<> checkForHide() const; | ||||||
|  | 
 | ||||||
| 	void refreshRecent() override; | 	void refreshRecent() override; | ||||||
| 	void preloadImages() override; | 	void preloadImages() override; | ||||||
| 	void clearSelection() override; | 	void clearSelection() override; | ||||||
|  | @ -82,15 +84,6 @@ protected: | ||||||
| 	void processPanelHideFinished() override; | 	void processPanelHideFinished() override; | ||||||
| 	int countDesiredHeight(int newWidth) override; | 	int countDesiredHeight(int newWidth) override; | ||||||
| 
 | 
 | ||||||
| private slots: |  | ||||||
| 	void onSettings(); |  | ||||||
| 	void onPreview(); |  | ||||||
| 
 |  | ||||||
| signals: |  | ||||||
| 	void selected(not_null<DocumentData*> sticker); |  | ||||||
| 	void scrollUpdated(); |  | ||||||
| 	void checkForHide(); |  | ||||||
| 
 |  | ||||||
| private: | private: | ||||||
| 	class Footer; | 	class Footer; | ||||||
| 
 | 
 | ||||||
|  | @ -248,6 +241,8 @@ private: | ||||||
| 	void fillCloudSearchRows(const std::vector<uint64> &cloudSets); | 	void fillCloudSearchRows(const std::vector<uint64> &cloudSets); | ||||||
| 	void addSearchRow(not_null<const Stickers::Set*> set); | 	void addSearchRow(not_null<const Stickers::Set*> set); | ||||||
| 
 | 
 | ||||||
|  | 	void showPreview(); | ||||||
|  | 
 | ||||||
| 	ChannelData *_megagroupSet = nullptr; | 	ChannelData *_megagroupSet = nullptr; | ||||||
| 	std::vector<Set> _mySets; | 	std::vector<Set> _mySets; | ||||||
| 	std::vector<Set> _featuredSets; | 	std::vector<Set> _featuredSets; | ||||||
|  | @ -281,7 +276,7 @@ private: | ||||||
| 
 | 
 | ||||||
| 	object_ptr<Ui::LinkButton> _settings; | 	object_ptr<Ui::LinkButton> _settings; | ||||||
| 
 | 
 | ||||||
| 	QTimer _previewTimer; | 	base::Timer _previewTimer; | ||||||
| 	bool _previewShown = false; | 	bool _previewShown = false; | ||||||
| 
 | 
 | ||||||
| 	std::map<QString, std::vector<uint64>> _searchCache; | 	std::map<QString, std::vector<uint64>> _searchCache; | ||||||
|  | @ -290,6 +285,10 @@ private: | ||||||
| 	QString _searchQuery, _searchNextQuery; | 	QString _searchQuery, _searchNextQuery; | ||||||
| 	mtpRequestId _searchRequestId = 0; | 	mtpRequestId _searchRequestId = 0; | ||||||
| 
 | 
 | ||||||
|  | 	rpl::event_stream<not_null<DocumentData*>> _chosen; | ||||||
|  | 	rpl::event_stream<> _scrollUpdated; | ||||||
|  | 	rpl::event_stream<> _checkForHide; | ||||||
|  | 
 | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| } // namespace ChatHelpers
 | } // namespace ChatHelpers
 | ||||||
|  |  | ||||||
|  | @ -66,24 +66,31 @@ TabbedPanel::TabbedPanel( | ||||||
| 
 | 
 | ||||||
| 	_hideTimer.setCallback([this] { hideByTimerOrLeave(); }); | 	_hideTimer.setCallback([this] { hideByTimerOrLeave(); }); | ||||||
| 
 | 
 | ||||||
| 	connect(_selector, &TabbedSelector::checkForHide, this, [this] { | 	_selector->checkForHide( | ||||||
|  | 	) | rpl::start_with_next([=] { | ||||||
| 		if (!rect().contains(mapFromGlobal(QCursor::pos()))) { | 		if (!rect().contains(mapFromGlobal(QCursor::pos()))) { | ||||||
| 			_hideTimer.callOnce(kDelayedHideTimeoutMs); | 			_hideTimer.callOnce(kDelayedHideTimeoutMs); | ||||||
| 		} | 		} | ||||||
| 	}); | 	}, lifetime()); | ||||||
| 	connect(_selector, &TabbedSelector::cancelled, this, [this] { | 
 | ||||||
|  | 	_selector->cancelled( | ||||||
|  | 	) | rpl::start_with_next([=] { | ||||||
| 		hideAnimated(); | 		hideAnimated(); | ||||||
| 	}); | 	}, lifetime()); | ||||||
| 	connect(_selector, &TabbedSelector::slideFinished, this, [this] { | 
 | ||||||
| 		InvokeQueued(this, [this] { | 	_selector->slideFinished( | ||||||
|  | 	) | rpl::start_with_next([=] { | ||||||
|  | 		InvokeQueued(this, [=] { | ||||||
| 			if (_hideAfterSlide) { | 			if (_hideAfterSlide) { | ||||||
| 				startOpacityAnimation(true); | 				startOpacityAnimation(true); | ||||||
| 			} | 			} | ||||||
| 		}); | 		}); | ||||||
| 	}); | 	}, lifetime()); | ||||||
| 
 | 
 | ||||||
| 	if (cPlatform() == dbipMac || cPlatform() == dbipMacOld) { | 	if (cPlatform() == dbipMac || cPlatform() == dbipMacOld) { | ||||||
| 		connect(App::wnd()->windowHandle(), SIGNAL(activeChanged()), this, SLOT(onWndActiveChanged())); | 		connect(App::wnd()->windowHandle(), &QWindow::activeChanged, this, [=] { | ||||||
|  | 			windowActiveChanged(); | ||||||
|  | 		}); | ||||||
| 	} | 	} | ||||||
| 	setAttribute(Qt::WA_OpaquePaintEvent, false); | 	setAttribute(Qt::WA_OpaquePaintEvent, false); | ||||||
| 
 | 
 | ||||||
|  | @ -122,7 +129,7 @@ void TabbedPanel::updateContentHeight() { | ||||||
| 	update(); | 	update(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void TabbedPanel::onWndActiveChanged() { | void TabbedPanel::windowActiveChanged() { | ||||||
| 	if (!App::wnd()->windowHandle()->isActive() && !isHidden() && !preventAutoHide()) { | 	if (!App::wnd()->windowHandle()->isActive() && !isHidden() && !preventAutoHide()) { | ||||||
| 		hideAnimated(); | 		hideAnimated(); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | @ -22,9 +22,7 @@ namespace ChatHelpers { | ||||||
| 
 | 
 | ||||||
| class TabbedSelector; | class TabbedSelector; | ||||||
| 
 | 
 | ||||||
| class TabbedPanel : public Ui::RpWidget{ | class TabbedPanel : public Ui::RpWidget { | ||||||
| 	Q_OBJECT |  | ||||||
| 
 |  | ||||||
| public: | public: | ||||||
| 	TabbedPanel(QWidget *parent, not_null<Window::Controller*> controller); | 	TabbedPanel(QWidget *parent, not_null<Window::Controller*> controller); | ||||||
| 	TabbedPanel(QWidget *parent, not_null<Window::Controller*> controller, object_ptr<TabbedSelector> selector); | 	TabbedPanel(QWidget *parent, not_null<Window::Controller*> controller, object_ptr<TabbedSelector> selector); | ||||||
|  | @ -55,9 +53,6 @@ protected: | ||||||
| 	void paintEvent(QPaintEvent *e) override; | 	void paintEvent(QPaintEvent *e) override; | ||||||
| 	bool eventFilter(QObject *obj, QEvent *e) override; | 	bool eventFilter(QObject *obj, QEvent *e) override; | ||||||
| 
 | 
 | ||||||
| private slots: |  | ||||||
| 	void onWndActiveChanged(); |  | ||||||
| 
 |  | ||||||
| private: | private: | ||||||
| 	void hideByTimerOrLeave(); | 	void hideByTimerOrLeave(); | ||||||
| 	void moveByBottom(); | 	void moveByBottom(); | ||||||
|  | @ -65,6 +60,7 @@ private: | ||||||
| 		return !_selector; | 		return !_selector; | ||||||
| 	} | 	} | ||||||
| 	void showFromSelector(); | 	void showFromSelector(); | ||||||
|  | 	void windowActiveChanged(); | ||||||
| 
 | 
 | ||||||
| 	style::margins innerPadding() const; | 	style::margins innerPadding() const; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -62,13 +62,8 @@ TabbedSection::TabbedSection( | ||||||
| 	_selector->setGeometry(rect()); | 	_selector->setGeometry(rect()); | ||||||
| 	_selector->showStarted(); | 	_selector->showStarted(); | ||||||
| 	_selector->show(); | 	_selector->show(); | ||||||
| 	connect(_selector, &TabbedSelector::cancelled, this, [this] { | 	_selector->setAfterShownCallback(nullptr); | ||||||
| 		if (_cancelledCallback) { | 	_selector->setBeforeHidingCallback(nullptr); | ||||||
| 			_cancelledCallback(); |  | ||||||
| 		} |  | ||||||
| 	}); |  | ||||||
| 	_selector->setAfterShownCallback(Fn<void(SelectorTab)>()); |  | ||||||
| 	_selector->setBeforeHidingCallback(Fn<void(SelectorTab)>()); |  | ||||||
| 
 | 
 | ||||||
| 	setAttribute(Qt::WA_OpaquePaintEvent, true); | 	setAttribute(Qt::WA_OpaquePaintEvent, true); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -49,9 +49,6 @@ public: | ||||||
| 
 | 
 | ||||||
| 	void beforeHiding(); | 	void beforeHiding(); | ||||||
| 	void afterShown(); | 	void afterShown(); | ||||||
| 	void setCancelledCallback(Fn<void()> callback) { |  | ||||||
| 		_cancelledCallback = std::move(callback); |  | ||||||
| 	} |  | ||||||
| 
 | 
 | ||||||
| 	object_ptr<TabbedSelector> takeSelector(); | 	object_ptr<TabbedSelector> takeSelector(); | ||||||
| 	QPointer<TabbedSelector> getSelector() const; | 	QPointer<TabbedSelector> getSelector() const; | ||||||
|  | @ -77,7 +74,6 @@ protected: | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
| 	object_ptr<TabbedSelector> _selector; | 	object_ptr<TabbedSelector> _selector; | ||||||
| 	Fn<void()> _cancelledCallback; |  | ||||||
| 	Fn<void(object_ptr<TabbedSelector>)> _returnMethod; | 	Fn<void(object_ptr<TabbedSelector>)> _returnMethod; | ||||||
| 
 | 
 | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | @ -261,6 +261,7 @@ object_ptr<TabbedSelector::Inner> TabbedSelector::Tab::takeWidget() { | ||||||
| 
 | 
 | ||||||
| void TabbedSelector::Tab::returnWidget(object_ptr<Inner> widget) { | void TabbedSelector::Tab::returnWidget(object_ptr<Inner> widget) { | ||||||
| 	_widget = std::move(widget); | 	_widget = std::move(widget); | ||||||
|  | 
 | ||||||
| 	Ensures(_widget == _weak); | 	Ensures(_widget == _weak); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -293,29 +294,30 @@ TabbedSelector::TabbedSelector(QWidget *parent, not_null<Window::Controller*> co | ||||||
| 
 | 
 | ||||||
| 	for (auto &tab : _tabs) { | 	for (auto &tab : _tabs) { | ||||||
| 		auto widget = tab.widget(); | 		auto widget = tab.widget(); | ||||||
| 		connect(widget, &Inner::scrollToY, this, [this, tab = &tab](int y) { | 
 | ||||||
|  | 		widget->scrollToRequests( | ||||||
|  | 		) | rpl::start_with_next([=, tab = &tab](int y) { | ||||||
| 			if (tab == currentTab()) { | 			if (tab == currentTab()) { | ||||||
| 				scrollToY(y); | 				scrollToY(y); | ||||||
| 			} else { | 			} else { | ||||||
| 				tab->saveScrollTop(y); | 				tab->saveScrollTop(y); | ||||||
| 			} | 			} | ||||||
| 		}); | 		}, widget->lifetime()); | ||||||
| 		connect(widget, &Inner::disableScroll, this, [this, tab = &tab](bool disabled) { | 
 | ||||||
|  | 		widget->disableScrollRequests( | ||||||
|  | 		) | rpl::start_with_next([=, tab = &tab](bool disabled) { | ||||||
| 			if (tab == currentTab()) { | 			if (tab == currentTab()) { | ||||||
| 				_scroll->disableScroll(disabled); | 				_scroll->disableScroll(disabled); | ||||||
| 			} | 			} | ||||||
| 		}); | 		}, widget->lifetime()); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	connect(stickers(), SIGNAL(scrollUpdated()), this, SLOT(onScroll())); | 	rpl::merge( | ||||||
| 	connect(_scroll, SIGNAL(scrolled()), this, SLOT(onScroll())); | 		stickers()->scrollUpdated() | rpl::map([] { return 0; }), | ||||||
| 	connect(emoji(), SIGNAL(selected(EmojiPtr)), this, SIGNAL(emojiSelected(EmojiPtr))); | 		_scroll->scrollTopChanges() | ||||||
| 	connect(stickers(), SIGNAL(selected(not_null<DocumentData*>)), this, SIGNAL(stickerOrGifSelected(not_null<DocumentData*>))); | 	) | rpl::start_with_next([=] { | ||||||
| 	connect(stickers(), SIGNAL(checkForHide()), this, SIGNAL(checkForHide())); | 		handleScroll(); | ||||||
| 	connect(gifs(), SIGNAL(selected(not_null<DocumentData*>)), this, SIGNAL(stickerOrGifSelected(not_null<DocumentData*>))); | 	}, lifetime()); | ||||||
| 	connect(gifs(), SIGNAL(selected(not_null<PhotoData*>)), this, SIGNAL(photoSelected(not_null<PhotoData*>))); |  | ||||||
| 	connect(gifs(), SIGNAL(selected(not_null<InlineBots::Result*>,not_null<UserData*>)), this, SIGNAL(inlineResultSelected(not_null<InlineBots::Result*>,not_null<UserData*>))); |  | ||||||
| 	connect(gifs(), SIGNAL(cancelled()), this, SIGNAL(cancelled())); |  | ||||||
| 
 | 
 | ||||||
| 	_topShadow->raise(); | 	_topShadow->raise(); | ||||||
| 	_bottomShadow->raise(); | 	_bottomShadow->raise(); | ||||||
|  | @ -340,6 +342,35 @@ TabbedSelector::TabbedSelector(QWidget *parent, not_null<Window::Controller*> co | ||||||
| 	showAll(); | 	showAll(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | rpl::producer<EmojiPtr> TabbedSelector::emojiChosen() const { | ||||||
|  | 	return emoji()->chosen(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | rpl::producer<not_null<DocumentData*>> TabbedSelector::fileChosen() const { | ||||||
|  | 	return rpl::merge(stickers()->chosen(), gifs()->fileChosen()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | rpl::producer<not_null<PhotoData*>> TabbedSelector::photoChosen() const { | ||||||
|  | 	return gifs()->photoChosen(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | auto TabbedSelector::inlineResultChosen() const | ||||||
|  | -> rpl::producer<InlineChosen> { | ||||||
|  | 	return gifs()->inlineResultChosen(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | rpl::producer<> TabbedSelector::cancelled() const { | ||||||
|  | 	return gifs()->cancelRequests(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | rpl::producer<> TabbedSelector::checkForHide() const { | ||||||
|  | 	return stickers()->checkForHide(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | rpl::producer<> TabbedSelector::slideFinished() const { | ||||||
|  | 	return _slideFinished.events(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void TabbedSelector::resizeEvent(QResizeEvent *e) { | void TabbedSelector::resizeEvent(QResizeEvent *e) { | ||||||
| 	_tabsSlider->resizeToWidth(width()); | 	_tabsSlider->resizeToWidth(width()); | ||||||
| 	_tabsSlider->moveToLeft(0, 0); | 	_tabsSlider->moveToLeft(0, 0); | ||||||
|  | @ -408,7 +439,7 @@ void TabbedSelector::paintEvent(QPaintEvent *e) { | ||||||
| 		if (!_a_slide.animating()) { | 		if (!_a_slide.animating()) { | ||||||
| 			_slideAnimation.reset(); | 			_slideAnimation.reset(); | ||||||
| 			afterShown(); | 			afterShown(); | ||||||
| 			emit slideFinished(); | 			_slideFinished.fire({}); | ||||||
| 		} | 		} | ||||||
| 	} else { | 	} else { | ||||||
| 		paintContent(p); | 		paintContent(p); | ||||||
|  | @ -607,7 +638,7 @@ void TabbedSelector::hideForSliding() { | ||||||
| 	currentTab()->widget()->clearSelection(); | 	currentTab()->widget()->clearSelection(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void TabbedSelector::onScroll() { | void TabbedSelector::handleScroll() { | ||||||
| 	auto scrollTop = _scroll->scrollTop(); | 	auto scrollTop = _scroll->scrollTop(); | ||||||
| 	auto scrollBottom = scrollTop + _scroll->height(); | 	auto scrollBottom = scrollTop + _scroll->height(); | ||||||
| 	currentTab()->widget()->setVisibleTopBottom(scrollTop, scrollBottom); | 	currentTab()->widget()->setVisibleTopBottom(scrollTop, scrollBottom); | ||||||
|  | @ -712,7 +743,7 @@ void TabbedSelector::setWidgetToScrollArea() { | ||||||
| 
 | 
 | ||||||
| 	_scroll->disableScroll(false); | 	_scroll->disableScroll(false); | ||||||
| 	scrollToY(currentTab()->getScrollTop()); | 	scrollToY(currentTab()->getScrollTop()); | ||||||
| 	onScroll(); | 	handleScroll(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void TabbedSelector::scrollToY(int y) { | void TabbedSelector::scrollToY(int y) { | ||||||
|  | @ -729,6 +760,22 @@ TabbedSelector::Inner::Inner( | ||||||
| , _controller(controller) { | , _controller(controller) { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | rpl::producer<int> TabbedSelector::Inner::scrollToRequests() const { | ||||||
|  | 	return _scrollToRequests.events(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | rpl::producer<bool> TabbedSelector::Inner::disableScrollRequests() const { | ||||||
|  | 	return _disableScrollRequests.events(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void TabbedSelector::Inner::scrollTo(int y) { | ||||||
|  | 	_scrollToRequests.fire_copy(y); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void TabbedSelector::Inner::disableScroll(bool disabled) { | ||||||
|  | 	_disableScrollRequests.fire_copy(disabled); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void TabbedSelector::Inner::visibleTopBottomUpdated(int visibleTop, int visibleBottom) { | void TabbedSelector::Inner::visibleTopBottomUpdated(int visibleTop, int visibleBottom) { | ||||||
| 	_visibleTop = visibleTop; | 	_visibleTop = visibleTop; | ||||||
| 	_visibleBottom = visibleBottom; | 	_visibleBottom = visibleBottom; | ||||||
|  |  | ||||||
|  | @ -40,11 +40,23 @@ class StickersListWidget; | ||||||
| class GifsListWidget; | class GifsListWidget; | ||||||
| 
 | 
 | ||||||
| class TabbedSelector : public Ui::RpWidget, private base::Subscriber { | class TabbedSelector : public Ui::RpWidget, private base::Subscriber { | ||||||
| 	Q_OBJECT |  | ||||||
| 
 |  | ||||||
| public: | public: | ||||||
|  | 	struct InlineChosen { | ||||||
|  | 		not_null<InlineBots::Result*> result; | ||||||
|  | 		not_null<UserData*> bot; | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
| 	TabbedSelector(QWidget *parent, not_null<Window::Controller*> controller); | 	TabbedSelector(QWidget *parent, not_null<Window::Controller*> controller); | ||||||
| 
 | 
 | ||||||
|  | 	rpl::producer<EmojiPtr> emojiChosen() const; | ||||||
|  | 	rpl::producer<not_null<DocumentData*>> fileChosen() const; | ||||||
|  | 	rpl::producer<not_null<PhotoData*>> photoChosen() const; | ||||||
|  | 	rpl::producer<InlineChosen> inlineResultChosen() const; | ||||||
|  | 
 | ||||||
|  | 	rpl::producer<> cancelled() const; | ||||||
|  | 	rpl::producer<> checkForHide() const; | ||||||
|  | 	rpl::producer<> slideFinished() const; | ||||||
|  | 
 | ||||||
| 	void setRoundRadius(int radius); | 	void setRoundRadius(int radius); | ||||||
| 	void refreshStickers(); | 	void refreshStickers(); | ||||||
| 	void showMegagroupSet(ChannelData *megagroup); | 	void showMegagroupSet(ChannelData *megagroup); | ||||||
|  | @ -87,21 +99,6 @@ protected: | ||||||
| 	void paintEvent(QPaintEvent *e) override; | 	void paintEvent(QPaintEvent *e) override; | ||||||
| 	void resizeEvent(QResizeEvent *e) override; | 	void resizeEvent(QResizeEvent *e) override; | ||||||
| 
 | 
 | ||||||
| private slots: |  | ||||||
| 	void onScroll(); |  | ||||||
| 
 |  | ||||||
| signals: |  | ||||||
| 	void emojiSelected(EmojiPtr emoji); |  | ||||||
| 	void stickerOrGifSelected(not_null<DocumentData*> sticker); |  | ||||||
| 	void photoSelected(not_null<PhotoData*> photo); |  | ||||||
| 	void inlineResultSelected( |  | ||||||
| 		not_null<InlineBots::Result*> result, |  | ||||||
| 		not_null<UserData*> bot); |  | ||||||
| 
 |  | ||||||
| 	void cancelled(); |  | ||||||
| 	void slideFinished(); |  | ||||||
| 	void checkForHide(); |  | ||||||
| 
 |  | ||||||
| private: | private: | ||||||
| 	class Tab { | 	class Tab { | ||||||
| 	public: | 	public: | ||||||
|  | @ -145,6 +142,7 @@ private: | ||||||
| 	void checkRestrictedPeer(); | 	void checkRestrictedPeer(); | ||||||
| 	bool isRestrictedView(); | 	bool isRestrictedView(); | ||||||
| 	void updateRestrictedLabelGeometry(); | 	void updateRestrictedLabelGeometry(); | ||||||
|  | 	void handleScroll(); | ||||||
| 
 | 
 | ||||||
| 	QImage grabForAnimation(); | 	QImage grabForAnimation(); | ||||||
| 
 | 
 | ||||||
|  | @ -193,12 +191,11 @@ private: | ||||||
| 	Fn<void(SelectorTab)> _beforeHidingCallback; | 	Fn<void(SelectorTab)> _beforeHidingCallback; | ||||||
| 
 | 
 | ||||||
| 	rpl::event_stream<> _showRequests; | 	rpl::event_stream<> _showRequests; | ||||||
|  | 	rpl::event_stream<> _slideFinished; | ||||||
| 
 | 
 | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| class TabbedSelector::Inner : public Ui::RpWidget { | class TabbedSelector::Inner : public Ui::RpWidget { | ||||||
| 	Q_OBJECT |  | ||||||
| 
 |  | ||||||
| public: | public: | ||||||
| 	Inner(QWidget *parent, not_null<Window::Controller*> controller); | 	Inner(QWidget *parent, not_null<Window::Controller*> controller); | ||||||
| 
 | 
 | ||||||
|  | @ -222,11 +219,10 @@ public: | ||||||
| 	virtual void beforeHiding() { | 	virtual void beforeHiding() { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	virtual object_ptr<InnerFooter> createFooter() = 0; | 	rpl::producer<int> scrollToRequests() const; | ||||||
|  | 	rpl::producer<bool> disableScrollRequests() const; | ||||||
| 
 | 
 | ||||||
| signals: | 	virtual object_ptr<InnerFooter> createFooter() = 0; | ||||||
| 	void scrollToY(int y); |  | ||||||
| 	void disableScroll(bool disabled); |  | ||||||
| 
 | 
 | ||||||
| protected: | protected: | ||||||
| 	void visibleTopBottomUpdated( | 	void visibleTopBottomUpdated( | ||||||
|  | @ -246,6 +242,9 @@ protected: | ||||||
| 	virtual void processPanelHideFinished() { | 	virtual void processPanelHideFinished() { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	void scrollTo(int y); | ||||||
|  | 	void disableScroll(bool disabled); | ||||||
|  | 
 | ||||||
| private: | private: | ||||||
| 	not_null<Window::Controller*> _controller; | 	not_null<Window::Controller*> _controller; | ||||||
| 
 | 
 | ||||||
|  | @ -253,6 +252,9 @@ private: | ||||||
| 	int _visibleBottom = 0; | 	int _visibleBottom = 0; | ||||||
| 	int _minimalHeight = 0; | 	int _minimalHeight = 0; | ||||||
| 
 | 
 | ||||||
|  | 	rpl::event_stream<int> _scrollToRequests; | ||||||
|  | 	rpl::event_stream<bool> _disableScrollRequests; | ||||||
|  | 
 | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| class TabbedSelector::InnerFooter : public TWidget { | class TabbedSelector::InnerFooter : public TWidget { | ||||||
|  |  | ||||||
|  | @ -477,14 +477,9 @@ HistoryWidget::HistoryWidget( | ||||||
| 	connect(_field, SIGNAL(changed()), this, SLOT(onTextChange())); | 	connect(_field, SIGNAL(changed()), this, SLOT(onTextChange())); | ||||||
| 	connect(App::wnd()->windowHandle(), SIGNAL(visibleChanged(bool)), this, SLOT(onWindowVisibleChanged())); | 	connect(App::wnd()->windowHandle(), SIGNAL(visibleChanged(bool)), this, SLOT(onWindowVisibleChanged())); | ||||||
| 	connect(&_scrollTimer, SIGNAL(timeout()), this, SLOT(onScrollTimer())); | 	connect(&_scrollTimer, SIGNAL(timeout()), this, SLOT(onScrollTimer())); | ||||||
| 	connect( | 
 | ||||||
| 		_tabbedSelector, | 	initTabbedSelector(); | ||||||
| 		&TabbedSelector::emojiSelected, | 
 | ||||||
| 		_field, |  | ||||||
| 		[=](EmojiPtr emoji) { InsertEmojiToField(_field, emoji); }); |  | ||||||
| 	connect(_tabbedSelector, SIGNAL(stickerOrGifSelected(not_null<DocumentData*>)), this, SLOT(onStickerOrGifSend(not_null<DocumentData*>))); |  | ||||||
| 	connect(_tabbedSelector, SIGNAL(photoSelected(not_null<PhotoData*>)), this, SLOT(onPhotoSend(not_null<PhotoData*>))); |  | ||||||
| 	connect(_tabbedSelector, SIGNAL(inlineResultSelected(not_null<InlineBots::Result*>,not_null<UserData*>)), this, SLOT(onInlineResultSend(not_null<InlineBots::Result*>,not_null<UserData*>))); |  | ||||||
| 	connect(Media::Capture::instance(), SIGNAL(error()), this, SLOT(onRecordError())); | 	connect(Media::Capture::instance(), SIGNAL(error()), this, SLOT(onRecordError())); | ||||||
| 	connect(Media::Capture::instance(), SIGNAL(updated(quint16,qint32)), this, SLOT(onRecordUpdate(quint16,qint32))); | 	connect(Media::Capture::instance(), SIGNAL(updated(quint16,qint32)), this, SLOT(onRecordUpdate(quint16,qint32))); | ||||||
| 	connect(Media::Capture::instance(), SIGNAL(done(QByteArray,VoiceWaveform,qint32)), this, SLOT(onRecordDone(QByteArray,VoiceWaveform,qint32))); | 	connect(Media::Capture::instance(), SIGNAL(done(QByteArray,VoiceWaveform,qint32)), this, SLOT(onRecordDone(QByteArray,VoiceWaveform,qint32))); | ||||||
|  | @ -532,7 +527,9 @@ HistoryWidget::HistoryWidget( | ||||||
| 	connect(_fieldAutocomplete, SIGNAL(mentionChosen(UserData*,FieldAutocomplete::ChooseMethod)), this, SLOT(onMentionInsert(UserData*))); | 	connect(_fieldAutocomplete, SIGNAL(mentionChosen(UserData*,FieldAutocomplete::ChooseMethod)), this, SLOT(onMentionInsert(UserData*))); | ||||||
| 	connect(_fieldAutocomplete, SIGNAL(hashtagChosen(QString,FieldAutocomplete::ChooseMethod)), this, SLOT(onHashtagOrBotCommandInsert(QString,FieldAutocomplete::ChooseMethod))); | 	connect(_fieldAutocomplete, SIGNAL(hashtagChosen(QString,FieldAutocomplete::ChooseMethod)), this, SLOT(onHashtagOrBotCommandInsert(QString,FieldAutocomplete::ChooseMethod))); | ||||||
| 	connect(_fieldAutocomplete, SIGNAL(botCommandChosen(QString,FieldAutocomplete::ChooseMethod)), this, SLOT(onHashtagOrBotCommandInsert(QString,FieldAutocomplete::ChooseMethod))); | 	connect(_fieldAutocomplete, SIGNAL(botCommandChosen(QString,FieldAutocomplete::ChooseMethod)), this, SLOT(onHashtagOrBotCommandInsert(QString,FieldAutocomplete::ChooseMethod))); | ||||||
| 	connect(_fieldAutocomplete, SIGNAL(stickerChosen(not_null<DocumentData*>,FieldAutocomplete::ChooseMethod)), this, SLOT(onStickerOrGifSend(not_null<DocumentData*>))); | 	connect(_fieldAutocomplete, &FieldAutocomplete::stickerChosen, this, [=](not_null<DocumentData*> document) { | ||||||
|  | 		sendExistingDocument(document); | ||||||
|  | 	}); | ||||||
| 	connect(_fieldAutocomplete, SIGNAL(moderateKeyActivate(int,bool*)), this, SLOT(onModerateKeyActivate(int,bool*))); | 	connect(_fieldAutocomplete, SIGNAL(moderateKeyActivate(int,bool*)), this, SLOT(onModerateKeyActivate(int,bool*))); | ||||||
| 	if (_supportAutocomplete) { | 	if (_supportAutocomplete) { | ||||||
| 		supportInitAutocomplete(); | 		supportInitAutocomplete(); | ||||||
|  | @ -779,6 +776,28 @@ HistoryWidget::HistoryWidget( | ||||||
| 	setupShortcuts(); | 	setupShortcuts(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void HistoryWidget::initTabbedSelector() { | ||||||
|  | 	_tabbedSelector->emojiChosen( | ||||||
|  | 	) | rpl::start_with_next([=](EmojiPtr emoji) { | ||||||
|  | 		InsertEmojiToField(_field, emoji); | ||||||
|  | 	}, lifetime()); | ||||||
|  | 
 | ||||||
|  | 	_tabbedSelector->fileChosen( | ||||||
|  | 	) | rpl::start_with_next([=](not_null<DocumentData*> document) { | ||||||
|  | 		sendExistingDocument(document); | ||||||
|  | 	}, lifetime()); | ||||||
|  | 
 | ||||||
|  | 	_tabbedSelector->photoChosen( | ||||||
|  | 	) | rpl::start_with_next([=](not_null<PhotoData*> photo) { | ||||||
|  | 		sendExistingPhoto(photo); | ||||||
|  | 	}, lifetime()); | ||||||
|  | 
 | ||||||
|  | 	_tabbedSelector->inlineResultChosen( | ||||||
|  | 	) | rpl::start_with_next([=](TabbedSelector::InlineChosen data) { | ||||||
|  | 		sendInlineResult(data.result, data.bot); | ||||||
|  | 	}, lifetime()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void HistoryWidget::supportInitAutocomplete() { | void HistoryWidget::supportInitAutocomplete() { | ||||||
| 	_supportAutocomplete->hide(); | 	_supportAutocomplete->hide(); | ||||||
| 
 | 
 | ||||||
|  | @ -1148,8 +1167,10 @@ void HistoryWidget::applyInlineBotQuery(UserData *bot, const QString &query) { | ||||||
| 		} | 		} | ||||||
| 		if (!_inlineResults) { | 		if (!_inlineResults) { | ||||||
| 			_inlineResults.create(this, controller()); | 			_inlineResults.create(this, controller()); | ||||||
| 			_inlineResults->setResultSelectedCallback([this](InlineBots::Result *result, UserData *bot) { | 			_inlineResults->setResultSelectedCallback([=]( | ||||||
| 				onInlineResultSend(result, bot); | 					InlineBots::Result *result, | ||||||
|  | 					UserData *bot) { | ||||||
|  | 				sendInlineResult(result, bot); | ||||||
| 			}); | 			}); | ||||||
| 			updateControlsGeometry(); | 			updateControlsGeometry(); | ||||||
| 			orderWidgets(); | 			orderWidgets(); | ||||||
|  | @ -5488,34 +5509,7 @@ void HistoryWidget::onFieldTabbed() { | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool HistoryWidget::onStickerOrGifSend(not_null<DocumentData*> document) { | void HistoryWidget::sendInlineResult( | ||||||
| 	if (auto megagroup = _peer ? _peer->asMegagroup() : nullptr) { |  | ||||||
| 		if (megagroup->restricted(ChannelRestriction::f_send_stickers)) { |  | ||||||
| 			Ui::show( |  | ||||||
| 				Box<InformBox>(lang(lng_restricted_send_stickers)), |  | ||||||
| 				LayerOption::KeepOther); |  | ||||||
| 			return false; |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	return sendExistingDocument( |  | ||||||
| 		document, |  | ||||||
| 		document->stickerOrGifOrigin(), |  | ||||||
| 		TextWithEntities()); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void HistoryWidget::onPhotoSend(not_null<PhotoData*> photo) { |  | ||||||
| 	if (auto megagroup = _peer ? _peer->asMegagroup() : nullptr) { |  | ||||||
| 		if (megagroup->restricted(ChannelRestriction::f_send_media)) { |  | ||||||
| 			Ui::show( |  | ||||||
| 				Box<InformBox>(lang(lng_restricted_send_media)), |  | ||||||
| 				LayerOption::KeepOther); |  | ||||||
| 			return; |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	sendExistingPhoto(photo, TextWithEntities()); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void HistoryWidget::onInlineResultSend( |  | ||||||
| 		not_null<InlineBots::Result*> result, | 		not_null<InlineBots::Result*> result, | ||||||
| 		not_null<UserData*> bot) { | 		not_null<UserData*> bot) { | ||||||
| 	if (!_peer || !_peer->canWrite()) { | 	if (!_peer || !_peer->canWrite()) { | ||||||
|  | @ -5662,12 +5656,20 @@ void HistoryWidget::destroyPinnedBar() { | ||||||
| 
 | 
 | ||||||
| bool HistoryWidget::sendExistingDocument( | bool HistoryWidget::sendExistingDocument( | ||||||
| 		not_null<DocumentData*> document, | 		not_null<DocumentData*> document, | ||||||
| 		Data::FileOrigin origin, |  | ||||||
| 		TextWithEntities caption) { | 		TextWithEntities caption) { | ||||||
| 	if (!_peer || !_peer->canWrite()) { | 	if (const auto megagroup = _peer ? _peer->asMegagroup() : nullptr) { | ||||||
|  | 		if (megagroup->restricted(ChannelRestriction::f_send_stickers)) { | ||||||
|  | 			Ui::show( | ||||||
|  | 				Box<InformBox>(lang(lng_restricted_send_stickers)), | ||||||
|  | 				LayerOption::KeepOther); | ||||||
|  | 			return false; | ||||||
|  | 		} | ||||||
|  | 	} else if (!_peer || !_peer->canWrite()) { | ||||||
| 		return false; | 		return false; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	const auto origin = document->stickerOrGifOrigin(); | ||||||
|  | 
 | ||||||
| 	auto options = ApiWrap::SendOptions(_history); | 	auto options = ApiWrap::SendOptions(_history); | ||||||
| 	options.clearDraft = false; | 	options.clearDraft = false; | ||||||
| 	options.replyTo = replyToId(); | 	options.replyTo = replyToId(); | ||||||
|  | @ -5688,11 +5690,18 @@ bool HistoryWidget::sendExistingDocument( | ||||||
| 	return true; | 	return true; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void HistoryWidget::sendExistingPhoto( | bool HistoryWidget::sendExistingPhoto( | ||||||
| 		not_null<PhotoData*> photo, | 		not_null<PhotoData*> photo, | ||||||
| 		TextWithEntities caption) { | 		TextWithEntities caption) { | ||||||
| 	if (!_peer || !_peer->canWrite()) { | 	if (const auto megagroup = _peer ? _peer->asMegagroup() : nullptr) { | ||||||
| 		return; | 		if (megagroup->restricted(ChannelRestriction::f_send_media)) { | ||||||
|  | 			Ui::show( | ||||||
|  | 				Box<InformBox>(lang(lng_restricted_send_media)), | ||||||
|  | 				LayerOption::KeepOther); | ||||||
|  | 			return false; | ||||||
|  | 		} | ||||||
|  | 	} else if (!_peer || !_peer->canWrite()) { | ||||||
|  | 		return false; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	auto options = ApiWrap::SendOptions(_history); | 	auto options = ApiWrap::SendOptions(_history); | ||||||
|  | @ -5774,6 +5783,8 @@ void HistoryWidget::sendExistingPhoto( | ||||||
| 	hideSelectorControlsAnimated(); | 	hideSelectorControlsAnimated(); | ||||||
| 
 | 
 | ||||||
| 	_field->setFocus(); | 	_field->setFocus(); | ||||||
|  | 
 | ||||||
|  | 	return true; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void HistoryWidget::setFieldText( | void HistoryWidget::setFieldText( | ||||||
|  |  | ||||||
|  | @ -331,6 +331,13 @@ public: | ||||||
| 	void confirmDeleteSelected(); | 	void confirmDeleteSelected(); | ||||||
| 	void clearSelected(); | 	void clearSelected(); | ||||||
| 
 | 
 | ||||||
|  | 	bool sendExistingDocument( | ||||||
|  | 		not_null<DocumentData*> document, | ||||||
|  | 		TextWithEntities caption = TextWithEntities()); | ||||||
|  | 	bool sendExistingPhoto( | ||||||
|  | 		not_null<PhotoData*> photo, | ||||||
|  | 		TextWithEntities caption = TextWithEntities()); | ||||||
|  | 
 | ||||||
| 	// Float player interface.
 | 	// Float player interface.
 | ||||||
| 	bool wheelEventFromFloatPlayer(QEvent *e) override; | 	bool wheelEventFromFloatPlayer(QEvent *e) override; | ||||||
| 	QRect rectForFloatPlayer() const override; | 	QRect rectForFloatPlayer() const override; | ||||||
|  | @ -389,11 +396,6 @@ public slots: | ||||||
| 	void onTextChange(); | 	void onTextChange(); | ||||||
| 
 | 
 | ||||||
| 	void onFieldTabbed(); | 	void onFieldTabbed(); | ||||||
| 	bool onStickerOrGifSend(not_null<DocumentData*> document); |  | ||||||
| 	void onPhotoSend(not_null<PhotoData*> photo); |  | ||||||
| 	void onInlineResultSend( |  | ||||||
| 		not_null<InlineBots::Result*> result, |  | ||||||
| 		not_null<UserData*> bot); |  | ||||||
| 
 | 
 | ||||||
| 	void onWindowVisibleChanged(); | 	void onWindowVisibleChanged(); | ||||||
| 
 | 
 | ||||||
|  | @ -431,6 +433,8 @@ private: | ||||||
| 	using TabbedSelector = ChatHelpers::TabbedSelector; | 	using TabbedSelector = ChatHelpers::TabbedSelector; | ||||||
| 	using DragState = Storage::MimeDataState; | 	using DragState = Storage::MimeDataState; | ||||||
| 
 | 
 | ||||||
|  | 	void initTabbedSelector(); | ||||||
|  | 
 | ||||||
| 	void send(Qt::KeyboardModifiers modifiers = Qt::KeyboardModifiers()); | 	void send(Qt::KeyboardModifiers modifiers = Qt::KeyboardModifiers()); | ||||||
| 	void handlePendingHistoryUpdate(); | 	void handlePendingHistoryUpdate(); | ||||||
| 	void fullPeerUpdated(PeerData *peer); | 	void fullPeerUpdated(PeerData *peer); | ||||||
|  | @ -601,13 +605,9 @@ private: | ||||||
| 	void destroyPinnedBar(); | 	void destroyPinnedBar(); | ||||||
| 	void unpinDone(const MTPUpdates &updates); | 	void unpinDone(const MTPUpdates &updates); | ||||||
| 
 | 
 | ||||||
| 	bool sendExistingDocument( | 	void sendInlineResult( | ||||||
| 		not_null<DocumentData*> document, | 		not_null<InlineBots::Result*> result, | ||||||
| 		Data::FileOrigin origin, | 		not_null<UserData*> bot); | ||||||
| 		TextWithEntities caption); |  | ||||||
| 	void sendExistingPhoto( |  | ||||||
| 		not_null<PhotoData*> photo, |  | ||||||
| 		TextWithEntities caption); |  | ||||||
| 
 | 
 | ||||||
| 	void drawField(Painter &p, const QRect &rect); | 	void drawField(Painter &p, const QRect &rect); | ||||||
| 	void paintEditHeader(Painter &p, const QRect &rect, int left, int top) const; | 	void paintEditHeader(Painter &p, const QRect &rect, int left, int top) const; | ||||||
|  |  | ||||||
|  | @ -40,18 +40,14 @@ constexpr auto kInlineBotRequestDelay = 400; | ||||||
| } // namespace
 | } // namespace
 | ||||||
| 
 | 
 | ||||||
| Inner::Inner(QWidget *parent, not_null<Window::Controller*> controller) : TWidget(parent) | Inner::Inner(QWidget *parent, not_null<Window::Controller*> controller) : TWidget(parent) | ||||||
| , _controller(controller) { | , _controller(controller) | ||||||
|  | , _updateInlineItems([=] { updateInlineItems(); }) | ||||||
|  | , _previewTimer([=] { showPreview(); }) { | ||||||
| 	resize(st::emojiPanWidth - st::emojiScroll.width - st::buttonRadius, st::emojiPanMinHeight); | 	resize(st::emojiPanWidth - st::emojiScroll.width - st::buttonRadius, st::emojiPanMinHeight); | ||||||
| 
 | 
 | ||||||
| 	setMouseTracking(true); | 	setMouseTracking(true); | ||||||
| 	setAttribute(Qt::WA_OpaquePaintEvent); | 	setAttribute(Qt::WA_OpaquePaintEvent); | ||||||
| 
 | 
 | ||||||
| 	_previewTimer.setSingleShot(true); |  | ||||||
| 	connect(&_previewTimer, SIGNAL(timeout()), this, SLOT(onPreview())); |  | ||||||
| 
 |  | ||||||
| 	_updateInlineItems.setSingleShot(true); |  | ||||||
| 	connect(&_updateInlineItems, SIGNAL(timeout()), this, SLOT(onUpdateInlineItems())); |  | ||||||
| 
 |  | ||||||
| 	subscribe(Auth().downloaderTaskFinished(), [this] { | 	subscribe(Auth().downloaderTaskFinished(), [this] { | ||||||
| 		update(); | 		update(); | ||||||
| 	}); | 	}); | ||||||
|  | @ -194,11 +190,11 @@ void Inner::mousePressEvent(QMouseEvent *e) { | ||||||
| 
 | 
 | ||||||
| 	_pressed = _selected; | 	_pressed = _selected; | ||||||
| 	ClickHandler::pressed(); | 	ClickHandler::pressed(); | ||||||
| 	_previewTimer.start(QApplication::startDragTime()); | 	_previewTimer.callOnce(QApplication::startDragTime()); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void Inner::mouseReleaseEvent(QMouseEvent *e) { | void Inner::mouseReleaseEvent(QMouseEvent *e) { | ||||||
| 	_previewTimer.stop(); | 	_previewTimer.cancel(); | ||||||
| 
 | 
 | ||||||
| 	auto pressed = std::exchange(_pressed, -1); | 	auto pressed = std::exchange(_pressed, -1); | ||||||
| 	auto activated = ClickHandler::unpressed(); | 	auto activated = ClickHandler::unpressed(); | ||||||
|  | @ -572,7 +568,7 @@ void Inner::inlineItemRepaint(const ItemBase *layout) { | ||||||
| 	if (_lastScrolled + 100 <= ms) { | 	if (_lastScrolled + 100 <= ms) { | ||||||
| 		update(); | 		update(); | ||||||
| 	} else { | 	} else { | ||||||
| 		_updateInlineItems.start(_lastScrolled + 100 - ms); | 		_updateInlineItems.callOnce(_lastScrolled + 100 - ms); | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -682,7 +678,7 @@ void Inner::updateSelected() { | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void Inner::onPreview() { | void Inner::showPreview() { | ||||||
| 	if (_pressed < 0) return; | 	if (_pressed < 0) return; | ||||||
| 
 | 
 | ||||||
| 	int row = _pressed / MatrixRowShift, col = _pressed % MatrixRowShift; | 	int row = _pressed / MatrixRowShift, col = _pressed % MatrixRowShift; | ||||||
|  | @ -698,12 +694,12 @@ void Inner::onPreview() { | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void Inner::onUpdateInlineItems() { | void Inner::updateInlineItems() { | ||||||
| 	auto ms = getms(); | 	auto ms = getms(); | ||||||
| 	if (_lastScrolled + 100 <= ms) { | 	if (_lastScrolled + 100 <= ms) { | ||||||
| 		update(); | 		update(); | ||||||
| 	} else { | 	} else { | ||||||
| 		_updateInlineItems.start(_lastScrolled + 100 - ms); | 		_updateInlineItems.callOnce(_lastScrolled + 100 - ms); | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL | ||||||
| #include "ui/twidget.h" | #include "ui/twidget.h" | ||||||
| #include "ui/abstract_button.h" | #include "ui/abstract_button.h" | ||||||
| #include "ui/effects/panel_animation.h" | #include "ui/effects/panel_animation.h" | ||||||
|  | #include "base/timer.h" | ||||||
| #include "mtproto/sender.h" | #include "mtproto/sender.h" | ||||||
| #include "inline_bots/inline_bot_layout_item.h" | #include "inline_bots/inline_bot_layout_item.h" | ||||||
| 
 | 
 | ||||||
|  | @ -90,8 +91,6 @@ protected: | ||||||
| 	void enterFromChildEvent(QEvent *e, QWidget *child) override; | 	void enterFromChildEvent(QEvent *e, QWidget *child) override; | ||||||
| 
 | 
 | ||||||
| private slots: | private slots: | ||||||
| 	void onPreview(); |  | ||||||
| 	void onUpdateInlineItems(); |  | ||||||
| 	void onSwitchPm(); | 	void onSwitchPm(); | ||||||
| 
 | 
 | ||||||
| signals: | signals: | ||||||
|  | @ -101,6 +100,11 @@ private: | ||||||
| 	static constexpr bool kRefreshIconsScrollAnimation = true; | 	static constexpr bool kRefreshIconsScrollAnimation = true; | ||||||
| 	static constexpr bool kRefreshIconsNoAnimation = false; | 	static constexpr bool kRefreshIconsNoAnimation = false; | ||||||
| 
 | 
 | ||||||
|  | 	struct Row { | ||||||
|  | 		int height = 0; | ||||||
|  | 		QVector<ItemBase*> items; | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
| 	void updateSelected(); | 	void updateSelected(); | ||||||
| 	void checkRestrictedPeer(); | 	void checkRestrictedPeer(); | ||||||
| 	bool isRestrictedView(); | 	bool isRestrictedView(); | ||||||
|  | @ -109,30 +113,9 @@ private: | ||||||
| 
 | 
 | ||||||
| 	void refreshSwitchPmButton(const CacheEntry *entry); | 	void refreshSwitchPmButton(const CacheEntry *entry); | ||||||
| 
 | 
 | ||||||
| 	not_null<Window::Controller*> _controller; | 	void showPreview(); | ||||||
| 
 | 	void updateInlineItems(); | ||||||
| 	int _visibleTop = 0; |  | ||||||
| 	int _visibleBottom = 0; |  | ||||||
| 
 |  | ||||||
| 	UserData *_inlineBot = nullptr; |  | ||||||
| 	PeerData *_inlineQueryPeer = nullptr; |  | ||||||
| 	TimeMs _lastScrolled = 0; |  | ||||||
| 	QTimer _updateInlineItems; |  | ||||||
| 	bool _inlineWithThumb = false; |  | ||||||
| 
 |  | ||||||
| 	object_ptr<Ui::RoundButton> _switchPmButton = { nullptr }; |  | ||||||
| 	QString _switchPmStartToken; |  | ||||||
| 
 |  | ||||||
| 	object_ptr<Ui::FlatLabel> _restrictedLabel = { nullptr }; |  | ||||||
| 
 |  | ||||||
| 	struct Row { |  | ||||||
| 		int height = 0; |  | ||||||
| 		QVector<ItemBase*> items; |  | ||||||
| 	}; |  | ||||||
| 	QVector<Row> _rows; |  | ||||||
| 	void clearInlineRows(bool resultsDeleted); | 	void clearInlineRows(bool resultsDeleted); | ||||||
| 
 |  | ||||||
| 	std::map<Result*, std::unique_ptr<ItemBase>> _inlineLayouts; |  | ||||||
| 	ItemBase *layoutPrepareInlineResult(Result *result, int32 position); | 	ItemBase *layoutPrepareInlineResult(Result *result, int32 position); | ||||||
| 
 | 
 | ||||||
| 	bool inlineRowsAddItem(Result *result, Row &row, int32 &sumWidth); | 	bool inlineRowsAddItem(Result *result, Row &row, int32 &sumWidth); | ||||||
|  | @ -144,11 +127,31 @@ private: | ||||||
| 	int validateExistingInlineRows(const Results &results); | 	int validateExistingInlineRows(const Results &results); | ||||||
| 	void selectInlineResult(int row, int column); | 	void selectInlineResult(int row, int column); | ||||||
| 
 | 
 | ||||||
|  | 	not_null<Window::Controller*> _controller; | ||||||
|  | 
 | ||||||
|  | 	int _visibleTop = 0; | ||||||
|  | 	int _visibleBottom = 0; | ||||||
|  | 
 | ||||||
|  | 	UserData *_inlineBot = nullptr; | ||||||
|  | 	PeerData *_inlineQueryPeer = nullptr; | ||||||
|  | 	TimeMs _lastScrolled = 0; | ||||||
|  | 	base::Timer _updateInlineItems; | ||||||
|  | 	bool _inlineWithThumb = false; | ||||||
|  | 
 | ||||||
|  | 	object_ptr<Ui::RoundButton> _switchPmButton = { nullptr }; | ||||||
|  | 	QString _switchPmStartToken; | ||||||
|  | 
 | ||||||
|  | 	object_ptr<Ui::FlatLabel> _restrictedLabel = { nullptr }; | ||||||
|  | 
 | ||||||
|  | 	QVector<Row> _rows; | ||||||
|  | 
 | ||||||
|  | 	std::map<Result*, std::unique_ptr<ItemBase>> _inlineLayouts; | ||||||
|  | 
 | ||||||
| 	int _selected = -1; | 	int _selected = -1; | ||||||
| 	int _pressed = -1; | 	int _pressed = -1; | ||||||
| 	QPoint _lastMousePos; | 	QPoint _lastMousePos; | ||||||
| 
 | 
 | ||||||
| 	QTimer _previewTimer; | 	base::Timer _previewTimer; | ||||||
| 	bool _previewShown = false; | 	bool _previewShown = false; | ||||||
| 
 | 
 | ||||||
| 	Fn<void(Result *result, UserData *bot)> _resultSelectedCallback; | 	Fn<void(Result *result, UserData *bot)> _resultSelectedCallback; | ||||||
|  |  | ||||||
|  | @ -1447,7 +1447,7 @@ void MainWidget::onSendFileConfirm( | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool MainWidget::onSendSticker(DocumentData *document) { | bool MainWidget::onSendSticker(DocumentData *document) { | ||||||
| 	return _history->onStickerOrGifSend(document); | 	return _history->sendExistingDocument(document); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void MainWidget::dialogsCancelled() { | void MainWidget::dialogsCancelled() { | ||||||
|  |  | ||||||
|  | @ -204,6 +204,9 @@ public: | ||||||
| 	auto scrollTopValue() const { | 	auto scrollTopValue() const { | ||||||
| 		return _scrollTopUpdated.events_starting_with(scrollTop()); | 		return _scrollTopUpdated.events_starting_with(scrollTop()); | ||||||
| 	} | 	} | ||||||
|  | 	auto scrollTopChanges() const { | ||||||
|  | 		return _scrollTopUpdated.events(); | ||||||
|  | 	} | ||||||
| 
 | 
 | ||||||
| 	void scrollTo(ScrollToRequest request); | 	void scrollTo(ScrollToRequest request); | ||||||
| 	void scrollToWidget(not_null<QWidget*> widget); | 	void scrollToWidget(not_null<QWidget*> widget); | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue