 1e5b2d7c66
			
		
	
	
		1e5b2d7c66
		
	
	
	
	
		
			
			press data in drop call ClickHandler::unpressed() after drag->exec(). Members dropdown area made smaller (only the members/online text rect).
		
			
				
	
	
		
			622 lines
		
	
	
	
		
			18 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			622 lines
		
	
	
	
		
			18 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | |
| This file is part of Telegram Desktop,
 | |
| the official desktop version of Telegram messaging app, see https://telegram.org
 | |
| 
 | |
| Telegram Desktop is free software: you can redistribute it and/or modify
 | |
| it under the terms of the GNU General Public License as published by
 | |
| the Free Software Foundation, either version 3 of the License, or
 | |
| (at your option) any later version.
 | |
| 
 | |
| It is distributed in the hope that it will be useful,
 | |
| but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 | |
| GNU General Public License for more details.
 | |
| 
 | |
| In addition, as a special exception, the copyright holders give permission
 | |
| to link the code of portions of this program with the OpenSSL library.
 | |
| 
 | |
| Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
 | |
| Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
 | |
| */
 | |
| #include "stdafx.h"
 | |
| 
 | |
| #include "ui/flatlabel.h"
 | |
| #include "mainwindow.h"
 | |
| #include "lang.h"
 | |
| 
 | |
| namespace {
 | |
| 	TextParseOptions _labelOptions = {
 | |
| 		TextParseMultiline, // flags
 | |
| 		0, // maxw
 | |
| 		0, // maxh
 | |
| 		Qt::LayoutDirectionAuto, // dir
 | |
| 	};
 | |
| 	TextParseOptions _labelMarkedOptions = {
 | |
| 		TextParseMultiline | TextParseLinks | TextParseHashtags | TextParseMentions | TextParseBotCommands, // flags
 | |
| 		0, // maxw
 | |
| 		0, // maxh
 | |
| 		Qt::LayoutDirectionAuto, // dir
 | |
| 	};
 | |
| }
 | |
| 
 | |
| FlatLabel::FlatLabel(QWidget *parent, const style::flatLabel &st, const style::textStyle &tst) : TWidget(parent)
 | |
| , _text(st.width ? st.width : QFIXED_MAX)
 | |
| , _st(st)
 | |
| , _tst(tst)
 | |
| , _contextCopyText(lang(lng_context_copy_text)) {
 | |
| 	init();
 | |
| }
 | |
| 
 | |
| FlatLabel::FlatLabel(QWidget *parent, const QString &text, InitType initType, const style::flatLabel &st, const style::textStyle &tst) : TWidget(parent)
 | |
| , _text(st.width ? st.width : QFIXED_MAX)
 | |
| , _st(st)
 | |
| , _tst(tst)
 | |
| , _contextCopyText(lang(lng_context_copy_text)) {
 | |
| 	if (initType == InitType::Rich) {
 | |
| 		setRichText(text);
 | |
| 	} else {
 | |
| 		setText(text);
 | |
| 	}
 | |
| 	init();
 | |
| }
 | |
| 
 | |
| void FlatLabel::init() {
 | |
| 	_trippleClickTimer.setSingleShot(true);
 | |
| 
 | |
| 	_touchSelectTimer.setSingleShot(true);
 | |
| 	connect(&_touchSelectTimer, SIGNAL(timeout()), this, SLOT(onTouchSelect()));
 | |
| }
 | |
| 
 | |
| void FlatLabel::setText(const QString &text) {
 | |
| 	textstyleSet(&_tst);
 | |
| 	_text.setText(_st.font, text, _labelOptions);
 | |
| 	refreshSize();
 | |
| 	textstyleRestore();
 | |
| 	setMouseTracking(_selectable || _text.hasLinks());
 | |
| }
 | |
| 
 | |
| void FlatLabel::setRichText(const QString &text) {
 | |
| 	textstyleSet(&_tst);
 | |
| 	_text.setRichText(_st.font, text, _labelOptions);
 | |
| 	refreshSize();
 | |
| 	textstyleRestore();
 | |
| 	setMouseTracking(_selectable || _text.hasLinks());
 | |
| }
 | |
| 
 | |
| void FlatLabel::setMarkedText(const TextWithEntities &textWithEntities) {
 | |
| 	textstyleSet(&_tst);
 | |
| 	_text.setMarkedText(_st.font, textWithEntities, _labelMarkedOptions);
 | |
| 	refreshSize();
 | |
| 	textstyleRestore();
 | |
| 	setMouseTracking(_selectable || _text.hasLinks());
 | |
| }
 | |
| 
 | |
| void FlatLabel::setSelectable(bool selectable) {
 | |
| 	_selectable = selectable;
 | |
| 	setMouseTracking(_selectable || _text.hasLinks());
 | |
| }
 | |
| 
 | |
| void FlatLabel::setDoubleClickSelectsParagraph(bool doubleClickSelectsParagraph) {
 | |
| 	_doubleClickSelectsParagraph = doubleClickSelectsParagraph;
 | |
| }
 | |
| 
 | |
| void FlatLabel::setContextCopyText(const QString ©Text) {
 | |
| 	_contextCopyText = copyText;
 | |
| }
 | |
| 
 | |
| void FlatLabel::setExpandLinksMode(ExpandLinksMode mode) {
 | |
| 	_contextExpandLinksMode = mode;
 | |
| }
 | |
| 
 | |
| void FlatLabel::setBreakEverywhere(bool breakEverywhere) {
 | |
| 	_breakEverywhere = breakEverywhere;
 | |
| }
 | |
| 
 | |
| void FlatLabel::resizeToWidth(int32 width) {
 | |
| 	textstyleSet(&_tst);
 | |
| 	_allowedWidth = width;
 | |
| 	refreshSize();
 | |
| 	textstyleRestore();
 | |
| }
 | |
| 
 | |
| int FlatLabel::naturalWidth() const {
 | |
| 	return _text.maxWidth();
 | |
| }
 | |
| 
 | |
| int FlatLabel::countTextWidth() const {
 | |
| 	return _allowedWidth ? (_allowedWidth - _st.margin.left() - _st.margin.right()) : (_st.width ? _st.width : _text.maxWidth());
 | |
| }
 | |
| 
 | |
| int FlatLabel::countTextHeight(int textWidth) {
 | |
| 	_fullTextHeight = _text.countHeight(textWidth);
 | |
| 	return _st.maxHeight ? qMin(_fullTextHeight, _st.maxHeight) : _fullTextHeight;
 | |
| }
 | |
| 
 | |
| void FlatLabel::refreshSize() {
 | |
| 	int textWidth = countTextWidth();
 | |
| 	int textHeight = countTextHeight(textWidth);
 | |
| 	int fullWidth = _st.margin.left() + textWidth + _st.margin.right();
 | |
| 	int fullHeight = _st.margin.top() + textHeight + _st.margin.bottom();
 | |
| 	resize(fullWidth, fullHeight);
 | |
| }
 | |
| 
 | |
| void FlatLabel::setLink(uint16 lnkIndex, const ClickHandlerPtr &lnk) {
 | |
| 	_text.setLink(lnkIndex, lnk);
 | |
| }
 | |
| 
 | |
| void FlatLabel::setClickHandlerHook(ClickHandlerHook &&hook) {
 | |
| 	_clickHandlerHook = std_::move(hook);
 | |
| }
 | |
| 
 | |
| void FlatLabel::mouseMoveEvent(QMouseEvent *e) {
 | |
| 	_lastMousePos = e->globalPos();
 | |
| 	dragActionUpdate();
 | |
| }
 | |
| 
 | |
| void FlatLabel::mousePressEvent(QMouseEvent *e) {
 | |
| 	if (_contextMenu) {
 | |
| 		e->accept();
 | |
| 		return; // ignore mouse press, that was hiding context menu
 | |
| 	}
 | |
| 	dragActionStart(e->globalPos(), e->button());
 | |
| }
 | |
| 
 | |
| Text::StateResult FlatLabel::dragActionStart(const QPoint &p, Qt::MouseButton button) {
 | |
| 	_lastMousePos = p;
 | |
| 	auto state = dragActionUpdate();
 | |
| 
 | |
| 	if (button != Qt::LeftButton) return state;
 | |
| 
 | |
| 	ClickHandler::pressed();
 | |
| 	_dragAction = NoDrag;
 | |
| 	_dragWasInactive = App::wnd()->inactivePress();
 | |
| 	if (_dragWasInactive) App::wnd()->inactivePress(false);
 | |
| 
 | |
| 	if (ClickHandler::getPressed()) {
 | |
| 		_dragStartPosition = mapFromGlobal(_lastMousePos);
 | |
| 		_dragAction = PrepareDrag;
 | |
| 	}
 | |
| 	if (!_selectable || _dragAction != NoDrag) {
 | |
| 		return state;
 | |
| 	}
 | |
| 
 | |
| 	if (_trippleClickTimer.isActive() && (_lastMousePos - _trippleClickPoint).manhattanLength() < QApplication::startDragDistance()) {
 | |
| 		if (state.uponSymbol) {
 | |
| 			_selection = { state.symbol, state.symbol };
 | |
| 			_savedSelection = { 0, 0 };
 | |
| 			_dragSymbol = state.symbol;
 | |
| 			_dragAction = Selecting;
 | |
| 			_selectionType = TextSelectType::Paragraphs;
 | |
| 			updateHover(state);
 | |
| 			_trippleClickTimer.start(QApplication::doubleClickInterval());
 | |
| 			update();
 | |
| 		}
 | |
| 	}
 | |
| 	if (_selectionType != TextSelectType::Paragraphs) {
 | |
| 		_dragSymbol = state.symbol;
 | |
| 		bool uponSelected = state.uponSymbol;
 | |
| 		if (uponSelected) {
 | |
| 			if (_dragSymbol < _selection.from || _dragSymbol >= _selection.to) {
 | |
| 				uponSelected = false;
 | |
| 			}
 | |
| 		}
 | |
| 		if (uponSelected) {
 | |
| 			_dragStartPosition = mapFromGlobal(_lastMousePos);
 | |
| 			_dragAction = PrepareDrag; // start text drag
 | |
| 		} else if (!_dragWasInactive) {
 | |
| 			if (state.afterSymbol) ++_dragSymbol;
 | |
| 			_selection = { _dragSymbol, _dragSymbol };
 | |
| 			_savedSelection = { 0, 0 };
 | |
| 			_dragAction = Selecting;
 | |
| 			update();
 | |
| 		}
 | |
| 	}
 | |
| 	return state;
 | |
| }
 | |
| 
 | |
| Text::StateResult FlatLabel::dragActionFinish(const QPoint &p, Qt::MouseButton button) {
 | |
| 	_lastMousePos = p;
 | |
| 	auto state = dragActionUpdate();
 | |
| 
 | |
| 	ClickHandlerPtr activated = ClickHandler::unpressed();
 | |
| 	if (_dragAction == Dragging) {
 | |
| 		activated.clear();
 | |
| 	} else if (_dragAction == PrepareDrag) {
 | |
| 		_selection = { 0, 0 };
 | |
| 		_savedSelection = { 0, 0 };
 | |
| 		update();
 | |
| 	}
 | |
| 	_dragAction = NoDrag;
 | |
| 	_selectionType = TextSelectType::Letters;
 | |
| 
 | |
| 	if (activated) {
 | |
| 		if (_clickHandlerHook.isNull() || _clickHandlerHook.call(activated, button)) {
 | |
| 			App::activateClickHandler(activated, button);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| #if defined Q_OS_LINUX32 || defined Q_OS_LINUX64
 | |
| 	if (!_selection.empty()) {
 | |
| 		QApplication::clipboard()->setText(_text.originalText(_selection, _contextExpandLinksMode), QClipboard::Selection);
 | |
| 	}
 | |
| #endif // Q_OS_LINUX32 || Q_OS_LINUX64
 | |
| 
 | |
| 	return state;
 | |
| }
 | |
| 
 | |
| void FlatLabel::mouseReleaseEvent(QMouseEvent *e) {
 | |
| 	dragActionFinish(e->globalPos(), e->button());
 | |
| 	if (!rect().contains(e->pos())) {
 | |
| 		leaveEvent(e);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void FlatLabel::mouseDoubleClickEvent(QMouseEvent *e) {
 | |
| 	auto state = dragActionStart(e->globalPos(), e->button());
 | |
| 	if (((_dragAction == Selecting) || (_dragAction == NoDrag)) && _selectionType == TextSelectType::Letters) {
 | |
| 		if (state.uponSymbol) {
 | |
| 			_dragSymbol = state.symbol;
 | |
| 			_selectionType = _doubleClickSelectsParagraph ? TextSelectType::Paragraphs : TextSelectType::Words;
 | |
| 			if (_dragAction == NoDrag) {
 | |
| 				_dragAction = Selecting;
 | |
| 				_selection = { state.symbol, state.symbol };
 | |
| 				_savedSelection = { 0, 0 };
 | |
| 			}
 | |
| 			mouseMoveEvent(e);
 | |
| 
 | |
| 			_trippleClickPoint = e->globalPos();
 | |
| 			_trippleClickTimer.start(QApplication::doubleClickInterval());
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void FlatLabel::enterEvent(QEvent *e) {
 | |
| 	_lastMousePos = QCursor::pos();
 | |
| 	dragActionUpdate();
 | |
| }
 | |
| 
 | |
| void FlatLabel::leaveEvent(QEvent *e) {
 | |
| 	ClickHandler::clearActive(this);
 | |
| }
 | |
| 
 | |
| void FlatLabel::focusOutEvent(QFocusEvent *e) {
 | |
| 	if (!_selection.empty()) {
 | |
| 		if (_contextMenu) {
 | |
| 			_savedSelection = _selection;
 | |
| 		}
 | |
| 		_selection = { 0, 0 };
 | |
| 		update();
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void FlatLabel::focusInEvent(QFocusEvent *e) {
 | |
| 	if (!_savedSelection.empty()) {
 | |
| 		_selection = _savedSelection;
 | |
| 		_savedSelection = { 0, 0 };
 | |
| 		update();
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void FlatLabel::keyPressEvent(QKeyEvent *e) {
 | |
| 	e->ignore();
 | |
| 	if (e->key() == Qt::Key_Copy || (e->key() == Qt::Key_C && e->modifiers().testFlag(Qt::ControlModifier))) {
 | |
| 		if (!_selection.empty()) {
 | |
| 			onCopySelectedText();
 | |
| 			e->accept();
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void FlatLabel::contextMenuEvent(QContextMenuEvent *e) {
 | |
| 	if (!_selectable) return;
 | |
| 
 | |
| 	showContextMenu(e, ContextMenuReason::FromEvent);
 | |
| }
 | |
| 
 | |
| bool FlatLabel::event(QEvent *e) {
 | |
| 	if (e->type() == QEvent::TouchBegin || e->type() == QEvent::TouchUpdate || e->type() == QEvent::TouchEnd || e->type() == QEvent::TouchCancel) {
 | |
| 		QTouchEvent *ev = static_cast<QTouchEvent*>(e);
 | |
| 		if (ev->device()->type() == QTouchDevice::TouchScreen) {
 | |
| 			touchEvent(ev);
 | |
| 			return true;
 | |
| 		}
 | |
| 	}
 | |
| 	return QWidget::event(e);
 | |
| }
 | |
| 
 | |
| void FlatLabel::touchEvent(QTouchEvent *e) {
 | |
| 	const Qt::TouchPointStates &states(e->touchPointStates());
 | |
| 	if (e->type() == QEvent::TouchCancel) { // cancel
 | |
| 		if (!_touchInProgress) return;
 | |
| 		_touchInProgress = false;
 | |
| 		_touchSelectTimer.stop();
 | |
| 		_touchSelect = false;
 | |
| 		_dragAction = NoDrag;
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	if (!e->touchPoints().isEmpty()) {
 | |
| 		_touchPrevPos = _touchPos;
 | |
| 		_touchPos = e->touchPoints().cbegin()->screenPos().toPoint();
 | |
| 	}
 | |
| 
 | |
| 	switch (e->type()) {
 | |
| 	case QEvent::TouchBegin:
 | |
| 		if (_contextMenu) {
 | |
| 			e->accept();
 | |
| 			return; // ignore mouse press, that was hiding context menu
 | |
| 		}
 | |
| 		if (_touchInProgress) return;
 | |
| 		if (e->touchPoints().isEmpty()) return;
 | |
| 
 | |
| 		_touchInProgress = true;
 | |
| 		_touchSelectTimer.start(QApplication::startDragTime());
 | |
| 		_touchSelect = false;
 | |
| 		_touchStart = _touchPrevPos = _touchPos;
 | |
| 	break;
 | |
| 
 | |
| 	case QEvent::TouchUpdate:
 | |
| 		if (!_touchInProgress) return;
 | |
| 		if (_touchSelect) {
 | |
| 			_lastMousePos = _touchPos;
 | |
| 			dragActionUpdate();
 | |
| 		}
 | |
| 	break;
 | |
| 
 | |
| 	case QEvent::TouchEnd:
 | |
| 		if (!_touchInProgress) return;
 | |
| 		_touchInProgress = false;
 | |
| 		if (_touchSelect) {
 | |
| 			dragActionFinish(_touchPos, Qt::RightButton);
 | |
| 			QContextMenuEvent contextMenu(QContextMenuEvent::Mouse, mapFromGlobal(_touchPos), _touchPos);
 | |
| 			showContextMenu(&contextMenu, ContextMenuReason::FromTouch);
 | |
| 		} else { // one short tap -- like mouse click
 | |
| 			dragActionStart(_touchPos, Qt::LeftButton);
 | |
| 			dragActionFinish(_touchPos, Qt::LeftButton);
 | |
| 		}
 | |
| 		_touchSelectTimer.stop();
 | |
| 		_touchSelect = false;
 | |
| 	break;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void FlatLabel::showContextMenu(QContextMenuEvent *e, ContextMenuReason reason) {
 | |
| 	if (_contextMenu) {
 | |
| 		_contextMenu->deleteLater();
 | |
| 		_contextMenu = nullptr;
 | |
| 	}
 | |
| 
 | |
| 	if (e->reason() == QContextMenuEvent::Mouse) {
 | |
| 		_lastMousePos = e->globalPos();
 | |
| 	} else {
 | |
| 		_lastMousePos = QCursor::pos();
 | |
| 	}
 | |
| 	auto state = dragActionUpdate();
 | |
| 
 | |
| 	bool hasSelection = !_selection.empty();
 | |
| 	bool uponSelection = state.uponSymbol && (state.symbol >= _selection.from) && (state.symbol < _selection.to);
 | |
| 	bool fullSelection = _text.isFullSelection(_selection);
 | |
| 	if (reason == ContextMenuReason::FromTouch && hasSelection && !uponSelection) {
 | |
| 		uponSelection = hasSelection;
 | |
| 	}
 | |
| 
 | |
| 	_contextMenu = new PopupMenu();
 | |
| 
 | |
| 	_contextMenuClickHandler = ClickHandler::getActive();
 | |
| 
 | |
| 	if (fullSelection && !_contextCopyText.isEmpty()) {
 | |
| 		_contextMenu->addAction(_contextCopyText, this, SLOT(onCopyContextText()))->setEnabled(true);
 | |
| 	} else if (uponSelection && !fullSelection) {
 | |
| 		_contextMenu->addAction(lang(lng_context_copy_selected), this, SLOT(onCopySelectedText()))->setEnabled(true);
 | |
| 	} else if (!hasSelection && !_contextCopyText.isEmpty()) {
 | |
| 		_contextMenu->addAction(_contextCopyText, this, SLOT(onCopyContextText()))->setEnabled(true);
 | |
| 	}
 | |
| 
 | |
| 	QString linkCopyToClipboardText = _contextMenuClickHandler ? _contextMenuClickHandler->copyToClipboardContextItemText() : QString();
 | |
| 	if (!linkCopyToClipboardText.isEmpty()) {
 | |
| 		_contextMenu->addAction(linkCopyToClipboardText, this, SLOT(onCopyContextUrl()))->setEnabled(true);
 | |
| 	}
 | |
| 
 | |
| 	if (_contextMenu->actions().isEmpty()) {
 | |
| 		delete _contextMenu;
 | |
| 		_contextMenu = nullptr;
 | |
| 	} else {
 | |
| 		connect(_contextMenu, SIGNAL(destroyed(QObject*)), this, SLOT(onContextMenuDestroy(QObject*)));
 | |
| 		_contextMenu->popup(e->globalPos());
 | |
| 		e->accept();
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void FlatLabel::onCopySelectedText() {
 | |
| 	auto selection = _selection.empty() ? (_contextMenu ? _savedSelection : _selection) : _selection;
 | |
| 	if (!selection.empty()) {
 | |
| 		QApplication::clipboard()->setText(_text.originalText(selection, _contextExpandLinksMode));
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void FlatLabel::onCopyContextText() {
 | |
| 	QApplication::clipboard()->setText(_text.originalText({ 0, 0xFFFF }, _contextExpandLinksMode));
 | |
| }
 | |
| 
 | |
| void FlatLabel::onCopyContextUrl() {
 | |
| 	if (_contextMenuClickHandler) {
 | |
| 		_contextMenuClickHandler->copyToClipboard();
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void FlatLabel::onTouchSelect() {
 | |
| 	_touchSelect = true;
 | |
| 	dragActionStart(_touchPos, Qt::LeftButton);
 | |
| }
 | |
| 
 | |
| void FlatLabel::onContextMenuDestroy(QObject *obj) {
 | |
| 	if (obj == _contextMenu) {
 | |
| 		_contextMenu = nullptr;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void FlatLabel::onExecuteDrag() {
 | |
| 	if (_dragAction != Dragging) return;
 | |
| 
 | |
| 	auto state = getTextState(_dragStartPosition);
 | |
| 	bool uponSelected = state.uponSymbol && _selection.from <= state.symbol;
 | |
| 	if (uponSelected) {
 | |
| 		if (_dragSymbol < _selection.from || _dragSymbol >= _selection.to) {
 | |
| 			uponSelected = false;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	ClickHandlerPtr pressedHandler = ClickHandler::getPressed();
 | |
| 	QString selectedText;
 | |
| 	if (uponSelected) {
 | |
| 		selectedText = _text.originalText(_selection, ExpandLinksAll);
 | |
| 	} else if (pressedHandler) {
 | |
| 		selectedText = pressedHandler->dragText();
 | |
| 	}
 | |
| 	if (!selectedText.isEmpty()) {
 | |
| 		auto mimeData = new QMimeData();
 | |
| 		mimeData->setText(selectedText);
 | |
| 		auto drag = new QDrag(App::wnd());
 | |
| 		drag->setMimeData(mimeData);
 | |
| 		drag->exec(Qt::CopyAction);
 | |
| 
 | |
| 		// We don't receive mouseReleaseEvent when drag is finished.
 | |
| 		ClickHandler::unpressed();
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void FlatLabel::clickHandlerActiveChanged(const ClickHandlerPtr &action, bool active) {
 | |
| 	update();
 | |
| }
 | |
| 
 | |
| void FlatLabel::clickHandlerPressedChanged(const ClickHandlerPtr &action, bool active) {
 | |
| 	update();
 | |
| }
 | |
| 
 | |
| Text::StateResult FlatLabel::dragActionUpdate() {
 | |
| 	auto m = mapFromGlobal(_lastMousePos);
 | |
| 	auto state = getTextState(m);
 | |
| 	updateHover(state);
 | |
| 
 | |
| 	if (_dragAction == PrepareDrag && (m - _dragStartPosition).manhattanLength() >= QApplication::startDragDistance()) {
 | |
| 		_dragAction = Dragging;
 | |
| 		QTimer::singleShot(1, this, SLOT(onExecuteDrag()));
 | |
| 	}
 | |
| 
 | |
| 	return state;
 | |
| }
 | |
| 
 | |
| void FlatLabel::updateHover(const Text::StateResult &state) {
 | |
| 	bool lnkChanged = ClickHandler::setActive(state.link, this);
 | |
| 
 | |
| 	if (!_selectable) {
 | |
| 		refreshCursor(state.uponSymbol);
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	Qt::CursorShape cur = style::cur_default;
 | |
| 	if (_dragAction == NoDrag) {
 | |
| 		if (state.link) {
 | |
| 			cur = style::cur_pointer;
 | |
| 		} else if (state.uponSymbol) {
 | |
| 			cur = style::cur_text;
 | |
| 		}
 | |
| 	} else {
 | |
| 		if (_dragAction == Selecting) {
 | |
| 			uint16 second = state.symbol;
 | |
| 			if (state.afterSymbol && _selectionType == TextSelectType::Letters) {
 | |
| 				++second;
 | |
| 			}
 | |
| 			auto selection = _text.adjustSelection({ qMin(second, _dragSymbol), qMax(second, _dragSymbol) }, _selectionType);
 | |
| 			if (_selection != selection) {
 | |
| 				_selection = selection;
 | |
| 				_savedSelection = { 0, 0 };
 | |
| 				setFocus();
 | |
| 				update();
 | |
| 			}
 | |
| 		} else if (_dragAction == Dragging) {
 | |
| 		}
 | |
| 
 | |
| 		if (ClickHandler::getPressed()) {
 | |
| 			cur = style::cur_pointer;
 | |
| 		} else if (_dragAction == Selecting) {
 | |
| 			cur = style::cur_text;
 | |
| 		}
 | |
| 	}
 | |
| 	if (_dragAction == Selecting) {
 | |
| //		checkSelectingScroll();
 | |
| 	} else {
 | |
| //		noSelectingScroll();
 | |
| 	}
 | |
| 
 | |
| 	if (_dragAction == NoDrag && (lnkChanged || cur != _cursor)) {
 | |
| 		setCursor(_cursor = cur);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void FlatLabel::refreshCursor(bool uponSymbol) {
 | |
| 	if (_dragAction != NoDrag) {
 | |
| 		return;
 | |
| 	}
 | |
| 	bool needTextCursor = _selectable && uponSymbol;
 | |
| 	style::cursor newCursor = needTextCursor ? style::cur_text : style::cur_default;
 | |
| 	if (ClickHandler::getActive()) {
 | |
| 		newCursor = style::cur_pointer;
 | |
| 	}
 | |
| 	if (newCursor != _cursor) {
 | |
| 		_cursor = newCursor;
 | |
| 		setCursor(_cursor);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| Text::StateResult FlatLabel::getTextState(const QPoint &m) const {
 | |
| 	Text::StateRequestElided request;
 | |
| 	request.align = _st.align;
 | |
| 	if (_selectable) {
 | |
| 		request.flags |= Text::StateRequest::Flag::LookupSymbol;
 | |
| 	}
 | |
| 	int textWidth = width() - _st.margin.left() - _st.margin.right();
 | |
| 
 | |
| 	textstyleSet(&_tst);
 | |
| 	Text::StateResult state;
 | |
| 	bool heightExceeded = _st.maxHeight && (_st.maxHeight < _fullTextHeight || textWidth < _text.maxWidth());
 | |
| 	bool renderElided = _breakEverywhere || heightExceeded;
 | |
| 	if (renderElided) {
 | |
| 		auto lineHeight = qMax(_tst.lineHeight, _st.font->height);
 | |
| 		auto lines = _st.maxHeight ? qMax(_st.maxHeight / lineHeight, 1) : ((height() / lineHeight) + 2);
 | |
| 		request.lines = lines;
 | |
| 		if (_breakEverywhere) {
 | |
| 			request.flags |= Text::StateRequest::Flag::BreakEverywhere;
 | |
| 		}
 | |
| 		state = _text.getStateElided(m.x() - _st.margin.left(), m.y() - _st.margin.top(), textWidth, request);
 | |
| 	} else {
 | |
| 		state = _text.getState(m.x() - _st.margin.left(), m.y() - _st.margin.top(), textWidth, request);
 | |
| 	}
 | |
| 	textstyleRestore();
 | |
| 
 | |
| 	return state;
 | |
| }
 | |
| 
 | |
| void FlatLabel::setOpacity(float64 o) {
 | |
| 	_opacity = o;
 | |
| 	update();
 | |
| }
 | |
| 
 | |
| void FlatLabel::paintEvent(QPaintEvent *e) {
 | |
| 	Painter p(this);
 | |
| 	p.setOpacity(_opacity);
 | |
| 	p.setPen(_st.textFg);
 | |
| 	textstyleSet(&_tst);
 | |
| 	int textWidth = width() - _st.margin.left() - _st.margin.right();
 | |
| 	auto selection = _selection.empty() ? (_contextMenu ? _savedSelection : _selection) : _selection;
 | |
| 	bool heightExceeded = _st.maxHeight && (_st.maxHeight < _fullTextHeight || textWidth < _text.maxWidth());
 | |
| 	bool renderElided = _breakEverywhere || heightExceeded;
 | |
| 	if (renderElided) {
 | |
| 		auto lineHeight = qMax(_tst.lineHeight, _st.font->height);
 | |
| 		auto lines = _st.maxHeight ? qMax(_st.maxHeight / lineHeight, 1) : ((height() / lineHeight) + 2);
 | |
| 		_text.drawElided(p, _st.margin.left(), _st.margin.top(), textWidth, lines, _st.align, e->rect().y(), e->rect().bottom(), 0, _breakEverywhere, selection);
 | |
| 	} else {
 | |
| 		_text.draw(p, _st.margin.left(), _st.margin.top(), textWidth, _st.align, e->rect().y(), e->rect().bottom(), selection);
 | |
| 	}
 | |
| 	textstyleRestore();
 | |
| }
 |