mirror of
https://github.com/telegramdesktop/tdesktop
synced 2025-01-01 03:22:10 +00:00
Enable distinct selecting of grouped media.
This commit is contained in:
parent
4c9931ab02
commit
537400d8b2
@ -224,7 +224,10 @@ QString documentSaveFilename(const DocumentData *data, bool forceSavingAs = fals
|
||||
return saveFileName(caption, filter, prefix, name, forceSavingAs, dir);
|
||||
}
|
||||
|
||||
void DocumentOpenClickHandler::doOpen(DocumentData *data, HistoryItem *context, ActionOnLoad action) {
|
||||
void DocumentOpenClickHandler::doOpen(
|
||||
not_null<DocumentData*> data,
|
||||
HistoryItem *context,
|
||||
ActionOnLoad action) {
|
||||
if (!data->date) return;
|
||||
|
||||
auto msgId = context ? context->fullId() : FullMsgId();
|
||||
@ -329,9 +332,13 @@ void DocumentOpenClickHandler::doOpen(DocumentData *data, HistoryItem *context,
|
||||
}
|
||||
|
||||
void DocumentOpenClickHandler::onClickImpl() const {
|
||||
const auto item = App::hoveredLinkItem()
|
||||
const auto item = context()
|
||||
? App::histItemById(context())
|
||||
: App::hoveredLinkItem()
|
||||
? App::hoveredLinkItem()
|
||||
: (App::contextItem() ? App::contextItem() : nullptr);
|
||||
: App::contextItem()
|
||||
? App::contextItem()
|
||||
: nullptr;
|
||||
const auto action = document()->isVoiceMessage()
|
||||
? ActionOnLoadNone
|
||||
: ActionOnLoadOpen;
|
||||
@ -339,13 +346,19 @@ void DocumentOpenClickHandler::onClickImpl() const {
|
||||
}
|
||||
|
||||
void GifOpenClickHandler::onClickImpl() const {
|
||||
const auto item = App::hoveredLinkItem()
|
||||
const auto item = context()
|
||||
? App::histItemById(context())
|
||||
: App::hoveredLinkItem()
|
||||
? App::hoveredLinkItem()
|
||||
: (App::contextItem() ? App::contextItem() : nullptr);
|
||||
: App::contextItem()
|
||||
? App::contextItem()
|
||||
: nullptr;
|
||||
doOpen(document(), item, ActionOnLoadPlayInline);
|
||||
}
|
||||
|
||||
void DocumentSaveClickHandler::doSave(DocumentData *data, bool forceSavingAs) {
|
||||
void DocumentSaveClickHandler::doSave(
|
||||
not_null<DocumentData*> data,
|
||||
bool forceSavingAs) {
|
||||
if (!data->date) return;
|
||||
|
||||
auto filepath = data->filepath(DocumentData::FilePathResolveSaveFromDataSilent, forceSavingAs);
|
||||
|
@ -311,15 +311,22 @@ QByteArray documentWaveformEncode5bit(const VoiceWaveform &waveform);
|
||||
|
||||
class DocumentClickHandler : public LeftButtonClickHandler {
|
||||
public:
|
||||
DocumentClickHandler(DocumentData *document)
|
||||
: _document(document) {
|
||||
DocumentClickHandler(
|
||||
not_null<DocumentData*> document,
|
||||
FullMsgId context = FullMsgId())
|
||||
: _document(document)
|
||||
, _context(context) {
|
||||
}
|
||||
DocumentData *document() const {
|
||||
not_null<DocumentData*> document() const {
|
||||
return _document;
|
||||
}
|
||||
FullMsgId context() const {
|
||||
return _context;
|
||||
}
|
||||
|
||||
private:
|
||||
DocumentData *_document;
|
||||
not_null<DocumentData*> _document;
|
||||
FullMsgId _context;
|
||||
|
||||
};
|
||||
|
||||
@ -327,7 +334,7 @@ class DocumentSaveClickHandler : public DocumentClickHandler {
|
||||
public:
|
||||
using DocumentClickHandler::DocumentClickHandler;
|
||||
static void doSave(
|
||||
DocumentData *document,
|
||||
not_null<DocumentData*> document,
|
||||
bool forceSavingAs = false);
|
||||
|
||||
protected:
|
||||
@ -339,7 +346,7 @@ class DocumentOpenClickHandler : public DocumentClickHandler {
|
||||
public:
|
||||
using DocumentClickHandler::DocumentClickHandler;
|
||||
static void doOpen(
|
||||
DocumentData *document,
|
||||
not_null<DocumentData*> document,
|
||||
HistoryItem *context,
|
||||
ActionOnLoad action = ActionOnLoadOpen);
|
||||
|
||||
|
@ -121,7 +121,7 @@ ImagePtr PhotoData::makeReplyPreview() {
|
||||
}
|
||||
|
||||
void PhotoOpenClickHandler::onClickImpl() const {
|
||||
Messenger::Instance().showPhoto(this, App::hoveredLinkItem() ? App::hoveredLinkItem() : App::contextItem());
|
||||
Messenger::Instance().showPhoto(this);
|
||||
}
|
||||
|
||||
void PhotoSaveClickHandler::onClickImpl() const {
|
||||
@ -136,13 +136,9 @@ void PhotoCancelClickHandler::onClickImpl() const {
|
||||
if (!data->date) return;
|
||||
|
||||
if (data->uploading()) {
|
||||
if (auto item = App::hoveredLinkItem() ? App::hoveredLinkItem() : (App::contextItem() ? App::contextItem() : nullptr)) {
|
||||
if (auto media = item->getMedia()) {
|
||||
if (media->type() == MediaTypePhoto && static_cast<HistoryPhoto*>(media)->photo() == data) {
|
||||
App::contextItem(item);
|
||||
App::main()->cancelUploadLayer();
|
||||
}
|
||||
}
|
||||
if (const auto item = App::histItemById(context())) {
|
||||
App::contextItem(item);
|
||||
App::main()->cancelUploadLayer();
|
||||
}
|
||||
} else {
|
||||
data->cancel();
|
||||
|
@ -74,8 +74,11 @@ class PhotoClickHandler : public LeftButtonClickHandler {
|
||||
public:
|
||||
PhotoClickHandler(
|
||||
not_null<PhotoData*> photo,
|
||||
FullMsgId context = FullMsgId(),
|
||||
PeerData *peer = nullptr)
|
||||
: _photo(photo), _peer(peer) {
|
||||
: _photo(photo)
|
||||
, _context(context)
|
||||
, _peer(peer) {
|
||||
}
|
||||
not_null<PhotoData*> photo() const {
|
||||
return _photo;
|
||||
@ -83,10 +86,14 @@ public:
|
||||
PeerData *peer() const {
|
||||
return _peer;
|
||||
}
|
||||
FullMsgId context() const {
|
||||
return _context;
|
||||
}
|
||||
|
||||
private:
|
||||
not_null<PhotoData*> _photo;
|
||||
PeerData *_peer;
|
||||
FullMsgId _context;
|
||||
PeerData *_peer = nullptr;
|
||||
|
||||
};
|
||||
|
||||
|
@ -363,6 +363,55 @@ void HistoryInner::enumerateDates(Method method) {
|
||||
enumerateItems<EnumItemsDirection::BottomToTop>(dateCallback);
|
||||
}
|
||||
|
||||
TextSelection HistoryInner::itemRenderSelection(
|
||||
not_null<HistoryItem*> item,
|
||||
int selfromy,
|
||||
int seltoy) const {
|
||||
Expects(!item->detached());
|
||||
|
||||
const auto y = item->block()->y() + item->y();
|
||||
if (y >= selfromy && y < seltoy) {
|
||||
if (_dragSelecting && !item->serviceMsg() && item->id > 0) {
|
||||
return FullSelection;
|
||||
}
|
||||
} else if (!_selected.empty()) {
|
||||
const auto itemSelection = [&](not_null<HistoryItem*> item) {
|
||||
auto i = _selected.find(item);
|
||||
if (i != _selected.end()) {
|
||||
return i->second;
|
||||
}
|
||||
return TextSelection();
|
||||
};
|
||||
const auto group = item->Get<HistoryMessageGroup>();
|
||||
if (group) {
|
||||
if (group->leader != item) {
|
||||
return TextSelection();
|
||||
}
|
||||
auto result = TextSelection();
|
||||
auto allFullSelected = true;
|
||||
const auto count = int(group->others.size());
|
||||
for (auto i = 0; i != count; ++i) {
|
||||
if (itemSelection(group->others[i]) == FullSelection) {
|
||||
result = AddGroupItemSelection(result, i);
|
||||
} else {
|
||||
allFullSelected = false;
|
||||
}
|
||||
}
|
||||
const auto leaderSelection = itemSelection(item);
|
||||
if (leaderSelection == FullSelection) {
|
||||
return allFullSelected
|
||||
? FullSelection
|
||||
: AddGroupItemSelection(result, count);
|
||||
} else if (leaderSelection != TextSelection()) {
|
||||
return leaderSelection;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return itemSelection(item);
|
||||
}
|
||||
return TextSelection();
|
||||
}
|
||||
|
||||
void HistoryInner::paintEvent(QPaintEvent *e) {
|
||||
if (Ui::skipPaintEvent(this, e)) {
|
||||
return;
|
||||
@ -399,9 +448,6 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
|
||||
|
||||
adjustCurrent(clip.top());
|
||||
|
||||
auto selEnd = _selected.cend();
|
||||
auto hasSel = !_selected.empty();
|
||||
|
||||
auto drawToY = clip.y() + clip.height();
|
||||
|
||||
auto selfromy = itemTop(_dragSelFrom);
|
||||
@ -425,18 +471,11 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
|
||||
p.save();
|
||||
p.translate(0, y);
|
||||
if (clip.y() < y + item->height()) while (y < drawToY) {
|
||||
TextSelection sel;
|
||||
if (y >= selfromy && y < seltoy) {
|
||||
if (_dragSelecting && !item->serviceMsg() && item->id > 0) {
|
||||
sel = FullSelection;
|
||||
}
|
||||
} else if (hasSel) {
|
||||
auto i = _selected.find(item);
|
||||
if (i != selEnd) {
|
||||
sel = i->second;
|
||||
}
|
||||
}
|
||||
item->draw(p, clip.translated(0, -y), sel, ms);
|
||||
const auto selection = itemRenderSelection(
|
||||
item,
|
||||
selfromy - mtop,
|
||||
seltoy - mtop);
|
||||
item->draw(p, clip.translated(0, -y), selection, ms);
|
||||
|
||||
if (item->hasViews()) {
|
||||
App::main()->scheduleViewIncrement(item);
|
||||
@ -469,25 +508,18 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
|
||||
auto iItem = (_curHistory == _history ? _curItem : 0);
|
||||
auto item = block->items[iItem];
|
||||
|
||||
auto historyRect = clip.intersected(QRect(0, hdrawtop, width(), clip.top() + clip.height()));
|
||||
auto hclip = clip.intersected(QRect(0, hdrawtop, width(), clip.top() + clip.height()));
|
||||
auto y = htop + block->y() + item->y();
|
||||
p.save();
|
||||
p.translate(0, y);
|
||||
while (y < drawToY) {
|
||||
auto h = item->height();
|
||||
if (historyRect.y() < y + h && hdrawtop < y + h) {
|
||||
TextSelection sel;
|
||||
if (y >= selfromy && y < seltoy) {
|
||||
if (_dragSelecting && !item->serviceMsg() && item->id > 0) {
|
||||
sel = FullSelection;
|
||||
}
|
||||
} else if (hasSel) {
|
||||
auto i = _selected.find(item);
|
||||
if (i != selEnd) {
|
||||
sel = i->second;
|
||||
}
|
||||
}
|
||||
item->draw(p, historyRect.translated(0, -y), sel, ms);
|
||||
if (hclip.y() < y + h && hdrawtop < y + h) {
|
||||
const auto selection = itemRenderSelection(
|
||||
item,
|
||||
selfromy - htop,
|
||||
seltoy - htop);
|
||||
item->draw(p, hclip.translated(0, -y), selection, ms);
|
||||
|
||||
if (item->hasViews()) {
|
||||
App::main()->scheduleViewIncrement(item);
|
||||
@ -830,7 +862,9 @@ void HistoryInner::mouseActionStart(const QPoint &screenPos, Qt::MouseButton but
|
||||
_mouseAction = MouseAction::PrepareDrag;
|
||||
} else if (!_selected.empty()) {
|
||||
if (_selected.cbegin()->second == FullSelection) {
|
||||
if (_selected.find(_mouseActionItem) != _selected.cend() && App::hoveredItem()) {
|
||||
if (_dragStateItem
|
||||
&& _selected.find(_dragStateItem) != _selected.cend()
|
||||
&& App::hoveredItem()) {
|
||||
_mouseAction = MouseAction::PrepareDrag; // start items drag
|
||||
} else if (!_pressWasInactive) {
|
||||
_mouseAction = MouseAction::PrepareSelect; // start items select
|
||||
@ -915,6 +949,7 @@ void HistoryInner::mouseActionStart(const QPoint &screenPos, Qt::MouseButton but
|
||||
|
||||
void HistoryInner::mouseActionCancel() {
|
||||
_mouseActionItem = nullptr;
|
||||
_dragStateItem = nullptr;
|
||||
_mouseAction = MouseAction::None;
|
||||
_dragStartPosition = QPoint(0, 0);
|
||||
_dragSelFrom = _dragSelTo = nullptr;
|
||||
@ -928,7 +963,8 @@ void HistoryInner::performDrag() {
|
||||
bool uponSelected = false;
|
||||
if (_mouseActionItem) {
|
||||
if (!_selected.empty() && _selected.cbegin()->second == FullSelection) {
|
||||
uponSelected = (_selected.find(_mouseActionItem) != _selected.cend());
|
||||
uponSelected = _dragStateItem
|
||||
&& (_selected.find(_dragStateItem) != _selected.cend());
|
||||
} else {
|
||||
HistoryStateRequest request;
|
||||
request.flags |= Text::StateRequest::Flag::LookupSymbol;
|
||||
@ -986,7 +1022,7 @@ void HistoryInner::performDrag() {
|
||||
forwardMimeType = qsl("application/x-td-forward-pressed");
|
||||
}
|
||||
}
|
||||
if (auto pressedLnkItem = App::pressedLinkItem()) {
|
||||
if (const auto pressedLnkItem = _dragStateItem) {
|
||||
if ((pressedMedia = pressedLnkItem->getMedia())) {
|
||||
if (forwardMimeType.isEmpty() && pressedMedia->dragItemByHandler(pressedHandler)) {
|
||||
forwardMimeType = qsl("application/x-td-forward-pressed-link");
|
||||
@ -1029,6 +1065,9 @@ void HistoryInner::itemRemoved(not_null<const HistoryItem*> item) {
|
||||
if (_mouseActionItem == item) {
|
||||
mouseActionCancel();
|
||||
}
|
||||
if (_dragStateItem == item) {
|
||||
_dragStateItem = nullptr;
|
||||
}
|
||||
|
||||
if (_dragSelFrom == item || _dragSelTo == item) {
|
||||
_dragSelFrom = 0;
|
||||
@ -1041,15 +1080,14 @@ void HistoryInner::itemRemoved(not_null<const HistoryItem*> item) {
|
||||
void HistoryInner::mouseActionFinish(const QPoint &screenPos, Qt::MouseButton button) {
|
||||
mouseActionUpdate(screenPos);
|
||||
|
||||
auto pressedLinkItem = App::pressedLinkItem();
|
||||
auto activated = ClickHandler::unpressed();
|
||||
if (_mouseAction == MouseAction::Dragging) {
|
||||
activated.clear();
|
||||
} else if (auto pressed = pressedLinkItem) {
|
||||
} else if (_mouseActionItem) {
|
||||
// if we are in selecting items mode perhaps we want to
|
||||
// toggle selection instead of activating the pressed link
|
||||
if (_mouseAction == MouseAction::PrepareDrag && !_pressWasInactive && !_selected.empty() && _selected.cbegin()->second == FullSelection && button != Qt::RightButton) {
|
||||
if (auto media = pressed->getMedia()) {
|
||||
if (auto media = _mouseActionItem->getMedia()) {
|
||||
if (media->toggleSelectionByHandlerClick(activated)) {
|
||||
activated.clear();
|
||||
}
|
||||
@ -1068,29 +1106,30 @@ void HistoryInner::mouseActionFinish(const QPoint &screenPos, Qt::MouseButton bu
|
||||
App::activateClickHandler(activated, button);
|
||||
return;
|
||||
}
|
||||
if (_mouseAction == MouseAction::PrepareSelect && !_pressWasInactive && !_selected.empty() && _selected.cbegin()->second == FullSelection) {
|
||||
auto i = _selected.find(_mouseActionItem);
|
||||
if (i == _selected.cend()) {
|
||||
if (!_mouseActionItem->serviceMsg()
|
||||
&& IsServerMsgId(_mouseActionItem->id)
|
||||
&& _selected.size() < MaxSelectedItems) {
|
||||
if (!_selected.empty() && _selected.cbegin()->second != FullSelection) {
|
||||
_selected.clear();
|
||||
}
|
||||
_selected.emplace(_mouseActionItem, FullSelection);
|
||||
}
|
||||
} else {
|
||||
_selected.erase(i);
|
||||
}
|
||||
if ((_mouseAction == MouseAction::PrepareSelect)
|
||||
&& !_pressWasInactive
|
||||
&& !_selected.empty()
|
||||
&& (_selected.cbegin()->second == FullSelection)) {
|
||||
changeDragSelection(
|
||||
&_selected,
|
||||
_mouseActionItem,
|
||||
SelectAction::Invert);
|
||||
repaintItem(_mouseActionItem);
|
||||
} else if (_mouseAction == MouseAction::PrepareDrag && !_pressWasInactive && button != Qt::RightButton) {
|
||||
auto i = _selected.find(_mouseActionItem);
|
||||
} else if ((_mouseAction == MouseAction::PrepareDrag)
|
||||
&& !_pressWasInactive
|
||||
&& _dragStateItem
|
||||
&& (button != Qt::RightButton)) {
|
||||
auto i = _selected.find(_dragStateItem);
|
||||
if (i != _selected.cend() && i->second == FullSelection) {
|
||||
_selected.erase(i);
|
||||
repaintItem(_mouseActionItem);
|
||||
} else if (i == _selected.cend() && !_mouseActionItem->serviceMsg() && _mouseActionItem->id > 0 && !_selected.empty() && _selected.cbegin()->second == FullSelection) {
|
||||
} else if ((i == _selected.cend())
|
||||
&& !_dragStateItem->serviceMsg()
|
||||
&& (_dragStateItem->id > 0)
|
||||
&& !_selected.empty()
|
||||
&& _selected.cbegin()->second == FullSelection) {
|
||||
if (_selected.size() < MaxSelectedItems) {
|
||||
_selected.emplace(_mouseActionItem, FullSelection);
|
||||
_selected.emplace(_dragStateItem, FullSelection);
|
||||
repaintItem(_mouseActionItem);
|
||||
}
|
||||
} else {
|
||||
@ -1117,7 +1156,8 @@ void HistoryInner::mouseActionFinish(const QPoint &screenPos, Qt::MouseButton bu
|
||||
|
||||
#if defined Q_OS_LINUX32 || defined Q_OS_LINUX64
|
||||
if (!_selected.empty() && _selected.cbegin()->second != FullSelection) {
|
||||
setToClipboard(_selected.cbegin()->first->selectedText(_selected.cbegin()->second), QClipboard::Selection);
|
||||
const auto [item, selection] = *_selected.cbegin();
|
||||
setToClipboard(item->selectedText(selection), QClipboard::Selection);
|
||||
}
|
||||
#endif // Q_OS_LINUX32 || Q_OS_LINUX64
|
||||
}
|
||||
@ -1206,9 +1246,9 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
|
||||
_menu = new Ui::PopupMenu(nullptr);
|
||||
|
||||
_contextMenuLink = ClickHandler::getActive();
|
||||
HistoryItem *item = App::hoveredItem() ? App::hoveredItem() : App::hoveredLinkItem();
|
||||
PhotoClickHandler *lnkPhoto = dynamic_cast<PhotoClickHandler*>(_contextMenuLink.data());
|
||||
DocumentClickHandler *lnkDocument = dynamic_cast<DocumentClickHandler*>(_contextMenuLink.data());
|
||||
auto item = App::hoveredItem() ? App::hoveredItem() : App::hoveredLinkItem();
|
||||
auto lnkPhoto = dynamic_cast<PhotoClickHandler*>(_contextMenuLink.data());
|
||||
auto lnkDocument = dynamic_cast<DocumentClickHandler*>(_contextMenuLink.data());
|
||||
auto lnkIsVideo = lnkDocument ? lnkDocument->document()->isVideoFile() : false;
|
||||
auto lnkIsVoice = lnkDocument ? lnkDocument->document()->isVoiceMessage() : false;
|
||||
auto lnkIsAudio = lnkDocument ? lnkDocument->document()->isAudioFile() : false;
|
||||
@ -1279,7 +1319,9 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
|
||||
}
|
||||
}
|
||||
if (App::hoveredLinkItem()->id > 0 && !App::hoveredLinkItem()->serviceMsg()) {
|
||||
_menu->addAction(lang(lng_context_select_msg), _widget, SLOT(selectMessage()))->setEnabled(true);
|
||||
_menu->addAction(lang(lng_context_select_msg), base::lambda_guarded(this, [this] {
|
||||
// TODO
|
||||
}))->setEnabled(true);
|
||||
}
|
||||
App::contextItem(App::hoveredLinkItem());
|
||||
}
|
||||
@ -1513,7 +1555,7 @@ void HistoryInner::saveContextGif() {
|
||||
}
|
||||
|
||||
void HistoryInner::copyContextText() {
|
||||
auto item = App::contextItem();
|
||||
const auto item = App::contextItem();
|
||||
if (!item || (item->getMedia() && item->getMedia()->type() == MediaTypeSticker)) {
|
||||
return;
|
||||
}
|
||||
@ -1532,24 +1574,24 @@ void HistoryInner::resizeEvent(QResizeEvent *e) {
|
||||
}
|
||||
|
||||
TextWithEntities HistoryInner::getSelectedText() const {
|
||||
SelectedItems sel = _selected;
|
||||
auto selected = _selected;
|
||||
|
||||
if (_mouseAction == MouseAction::Selecting && _dragSelFrom && _dragSelTo) {
|
||||
applyDragSelection(&sel);
|
||||
applyDragSelection(&selected);
|
||||
}
|
||||
|
||||
if (sel.empty()) {
|
||||
if (selected.empty()) {
|
||||
return TextWithEntities();
|
||||
}
|
||||
if (sel.cbegin()->second != FullSelection) {
|
||||
return sel.cbegin()->first->selectedText(sel.cbegin()->second);
|
||||
if (selected.cbegin()->second != FullSelection) {
|
||||
const auto [item, selection] = *selected.cbegin();
|
||||
return item->selectedText(selection);
|
||||
}
|
||||
|
||||
int fullSize = 0;
|
||||
QString timeFormat(qsl(", [dd.MM.yy hh:mm]\n"));
|
||||
QMap<int, TextWithEntities> texts;
|
||||
for (auto &selected : sel) {
|
||||
auto item = selected.first;
|
||||
for (const auto [item, selection] : selected) {
|
||||
if (item->detached()) continue;
|
||||
|
||||
auto time = item->date.toString(timeFormat);
|
||||
@ -2007,10 +2049,11 @@ MessageIdsList HistoryInner::getSelectedItems() const {
|
||||
return result;
|
||||
}
|
||||
|
||||
void HistoryInner::selectItem(HistoryItem *item) {
|
||||
void HistoryInner::selectItem(not_null<HistoryItem*> item) {
|
||||
if (!_selected.empty() && _selected.cbegin()->second != FullSelection) {
|
||||
_selected.clear();
|
||||
} else if (_selected.size() == MaxSelectedItems && _selected.find(item) == _selected.cend()) {
|
||||
} else if (_selected.size() == MaxSelectedItems
|
||||
&& _selected.find(item) == _selected.cend()) {
|
||||
return;
|
||||
}
|
||||
_selected.emplace(item, FullSelection);
|
||||
@ -2059,10 +2102,16 @@ void HistoryInner::onUpdateSelected() {
|
||||
|
||||
HistoryTextState dragState;
|
||||
ClickHandlerHost *lnkhost = nullptr;
|
||||
bool selectingText = (item == _mouseActionItem && item == App::hoveredItem() && !_selected.empty() && _selected.cbegin()->second != FullSelection);
|
||||
auto selectingText = (item == _mouseActionItem)
|
||||
&& (item == App::hoveredItem())
|
||||
&& !_selected.empty()
|
||||
&& (_selected.cbegin()->second != FullSelection);
|
||||
if (point.y() < _historyPaddingTop) {
|
||||
if (_botAbout && !_botAbout->info->text.isEmpty() && _botAbout->height > 0) {
|
||||
dragState = _botAbout->info->text.getState(point - _botAbout->rect.topLeft() - QPoint(st::msgPadding.left(), st::msgPadding.top() + st::botDescSkip + st::msgNameFont->height), _botAbout->width);
|
||||
dragState = HistoryTextState(nullptr, _botAbout->info->text.getState(
|
||||
point - _botAbout->rect.topLeft() - QPoint(st::msgPadding.left(), st::msgPadding.top() + st::botDescSkip + st::msgNameFont->height),
|
||||
_botAbout->width));
|
||||
_dragStateItem = App::histItemById(dragState.itemId);
|
||||
lnkhost = _botAbout.get();
|
||||
}
|
||||
} else if (item) {
|
||||
@ -2077,7 +2126,7 @@ void HistoryInner::onUpdateSelected() {
|
||||
|
||||
auto dateHeight = st::msgServicePadding.bottom() + st::msgServiceFont->height + st::msgServicePadding.top();
|
||||
auto scrollDateOpacity = _scrollDateOpacity.current(_scrollDateShown ? 1. : 0.);
|
||||
enumerateDates([this, &dragState, &lnkhost, &point, scrollDateOpacity, dateHeight/*, lastDate, showFloatingBefore*/](not_null<HistoryItem*> item, int itemtop, int dateTop) {
|
||||
enumerateDates([&](not_null<HistoryItem*> item, int itemtop, int dateTop) {
|
||||
// stop enumeration if the date is above our point
|
||||
if (dateTop + dateHeight <= point.y()) {
|
||||
return false;
|
||||
@ -2116,7 +2165,10 @@ void HistoryInner::onUpdateSelected() {
|
||||
} else {
|
||||
static_cast<DateClickHandler*>(_scrollDateLink.data())->setDate(item->date.date());
|
||||
}
|
||||
dragState.link = _scrollDateLink;
|
||||
dragState = HistoryTextState(
|
||||
nullptr,
|
||||
_scrollDateLink);
|
||||
_dragStateItem = App::histItemById(dragState.itemId);
|
||||
lnkhost = item;
|
||||
}
|
||||
}
|
||||
@ -2132,11 +2184,12 @@ void HistoryInner::onUpdateSelected() {
|
||||
selectingText = false;
|
||||
}
|
||||
dragState = item->getState(m, request);
|
||||
_dragStateItem = App::histItemById(dragState.itemId);
|
||||
lnkhost = item;
|
||||
if (!dragState.link && m.x() >= st::historyPhotoLeft && m.x() < st::historyPhotoLeft + st::msgPhotoSize) {
|
||||
if (auto msg = item->toHistoryMessage()) {
|
||||
if (msg->hasFromPhoto()) {
|
||||
enumerateUserpics([&dragState, &lnkhost, &point](not_null<HistoryMessage*> message, int userpicTop) -> bool {
|
||||
enumerateUserpics([&](not_null<HistoryMessage*> message, int userpicTop) -> bool {
|
||||
// stop enumeration if the userpic is below our point
|
||||
if (userpicTop > point.y()) {
|
||||
return false;
|
||||
@ -2144,7 +2197,10 @@ void HistoryInner::onUpdateSelected() {
|
||||
|
||||
// stop enumeration if we've found a userpic under the cursor
|
||||
if (point.y() >= userpicTop && point.y() < userpicTop + st::msgPhotoSize) {
|
||||
dragState.link = message->displayFrom()->openLink();
|
||||
dragState = HistoryTextState(
|
||||
nullptr,
|
||||
message->displayFrom()->openLink());
|
||||
_dragStateItem = App::histItemById(dragState.itemId);
|
||||
lnkhost = message;
|
||||
return false;
|
||||
}
|
||||
@ -2171,7 +2227,7 @@ void HistoryInner::onUpdateSelected() {
|
||||
} else if (_mouseCursorState == HistoryInTextCursorState && (_selected.empty() || _selected.cbegin()->second != FullSelection)) {
|
||||
cur = style::cur_text;
|
||||
} else if (_mouseCursorState == HistoryInDateCursorState) {
|
||||
// cur = style::cur_cross;
|
||||
//cur = style::cur_cross;
|
||||
}
|
||||
} else if (item) {
|
||||
if (_mouseAction == MouseAction::Selecting) {
|
||||
@ -2235,7 +2291,9 @@ void HistoryInner::onUpdateSelected() {
|
||||
|
||||
if (ClickHandler::getPressed()) {
|
||||
cur = style::cur_pointer;
|
||||
} else if (_mouseAction == MouseAction::Selecting && !_selected.empty() && _selected.cbegin()->second != FullSelection) {
|
||||
} else if ((_mouseAction == MouseAction::Selecting)
|
||||
&& !_selected.empty()
|
||||
&& (_selected.cbegin()->second != FullSelection)) {
|
||||
if (!_dragSelFrom || !_dragSelTo) {
|
||||
cur = style::cur_text;
|
||||
}
|
||||
@ -2243,7 +2301,7 @@ void HistoryInner::onUpdateSelected() {
|
||||
}
|
||||
|
||||
// Voice message seek support.
|
||||
if (auto pressedItem = App::pressedLinkItem()) {
|
||||
if (const auto pressedItem = _dragStateItem) {
|
||||
if (!pressedItem->detached()) {
|
||||
if (pressedItem->history() == _history || pressedItem->history() == _migrated) {
|
||||
auto adjustedPoint = mapPointToItem(point, pressedItem);
|
||||
@ -2370,25 +2428,125 @@ void HistoryInner::applyDragSelection() {
|
||||
applyDragSelection(&_selected);
|
||||
}
|
||||
|
||||
void HistoryInner::addSelectionRange(SelectedItems *toItems, int32 fromblock, int32 fromitem, int32 toblock, int32 toitem, History *h) const {
|
||||
bool HistoryInner::isFullSelected(
|
||||
not_null<SelectedItems*> toItems,
|
||||
not_null<HistoryItem*> item) const {
|
||||
const auto group = [&] {
|
||||
if (const auto group = item->Get<HistoryMessageGroup>()) {
|
||||
if (group->leader == item) {
|
||||
return group;
|
||||
}
|
||||
return group->leader->Get<HistoryMessageGroup>();
|
||||
}
|
||||
return (HistoryMessageGroup*)nullptr;
|
||||
}();
|
||||
const auto singleSelected = [&](not_null<HistoryItem*> item) {
|
||||
const auto i = toItems->find(item);
|
||||
return (i != toItems->cend()) && (i->second == FullSelection);
|
||||
};
|
||||
if (group) {
|
||||
if (!singleSelected(group->leader)) {
|
||||
return false;
|
||||
}
|
||||
for (const auto other : group->others) {
|
||||
if (!singleSelected(other)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else if (!singleSelected(item)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void HistoryInner::changeDragSelection(
|
||||
not_null<SelectedItems*> toItems,
|
||||
not_null<HistoryItem*> item,
|
||||
SelectAction action) const {
|
||||
if (action == SelectAction::Invert) {
|
||||
action = isFullSelected(toItems, item)
|
||||
? SelectAction::Deselect
|
||||
: SelectAction::Select;
|
||||
}
|
||||
auto total = toItems->size();
|
||||
const auto add = (action == SelectAction::Select);
|
||||
const auto goodForAdding = [&](not_null<HistoryItem*> item) {
|
||||
if (item->id <= 0 || item->serviceMsg()) {
|
||||
return false;
|
||||
}
|
||||
if (toItems->find(item) == toItems->end()) {
|
||||
++total;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
const auto addSingle = [&](not_null<HistoryItem*> item) {
|
||||
const auto i = toItems->find(item);
|
||||
if (i == toItems->cend()) {
|
||||
toItems->emplace(item, FullSelection);
|
||||
} else if (i->second != FullSelection) {
|
||||
i->second = FullSelection;
|
||||
}
|
||||
};
|
||||
const auto removeSingle = [&](not_null<HistoryItem*> item) {
|
||||
const auto i = toItems->find(item);
|
||||
if (i != toItems->cend()) {
|
||||
toItems->erase(i);
|
||||
}
|
||||
};
|
||||
const auto group = [&] {
|
||||
if (const auto group = item->Get<HistoryMessageGroup>()) {
|
||||
if (group->leader == item) {
|
||||
return group;
|
||||
}
|
||||
return group->leader->Get<HistoryMessageGroup>();
|
||||
}
|
||||
return (HistoryMessageGroup*)nullptr;
|
||||
}();
|
||||
if (group) {
|
||||
const auto adding = [&] {
|
||||
if (!add || !goodForAdding(group->leader)) {
|
||||
return false;
|
||||
}
|
||||
for (const auto other : group->others) {
|
||||
if (!goodForAdding(other)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return (total <= MaxSelectedItems);
|
||||
}();
|
||||
if (adding) {
|
||||
addSingle(group->leader);
|
||||
for (const auto other : group->others) {
|
||||
addSingle(other);
|
||||
}
|
||||
} else {
|
||||
removeSingle(group->leader);
|
||||
for (const auto other : group->others) {
|
||||
removeSingle(other);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (add && goodForAdding(item) && total <= MaxSelectedItems) {
|
||||
addSingle(item);
|
||||
} else {
|
||||
removeSingle(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HistoryInner::addSelectionRange(
|
||||
not_null<SelectedItems*> toItems,
|
||||
not_null<History*> history,
|
||||
int fromblock,
|
||||
int fromitem,
|
||||
int toblock,
|
||||
int toitem) const {
|
||||
if (fromblock >= 0 && fromitem >= 0 && toblock >= 0 && toitem >= 0) {
|
||||
for (; fromblock <= toblock; ++fromblock) {
|
||||
auto block = h->blocks[fromblock];
|
||||
for (int32 cnt = (fromblock < toblock) ? block->items.size() : (toitem + 1); fromitem < cnt; ++fromitem) {
|
||||
auto block = history->blocks[fromblock];
|
||||
for (int cnt = (fromblock < toblock) ? block->items.size() : (toitem + 1); fromitem < cnt; ++fromitem) {
|
||||
auto item = block->items[fromitem];
|
||||
auto i = toItems->find(item);
|
||||
if (item->id > 0 && !item->serviceMsg()) {
|
||||
if (i == toItems->cend()) {
|
||||
if (toItems->size() >= MaxSelectedItems) break;
|
||||
toItems->emplace(item, FullSelection);
|
||||
} else if (i->second != FullSelection) {
|
||||
i->second = FullSelection;
|
||||
}
|
||||
} else {
|
||||
if (i != toItems->cend()) {
|
||||
toItems->erase(i);
|
||||
}
|
||||
}
|
||||
changeDragSelection(toItems, item, SelectAction::Select);
|
||||
}
|
||||
if (toItems->size() >= MaxSelectedItems) break;
|
||||
fromitem = 0;
|
||||
@ -2396,27 +2554,33 @@ void HistoryInner::addSelectionRange(SelectedItems *toItems, int32 fromblock, in
|
||||
}
|
||||
}
|
||||
|
||||
void HistoryInner::applyDragSelection(SelectedItems *toItems) const {
|
||||
int32 selfromy = itemTop(_dragSelFrom), seltoy = itemTop(_dragSelTo);
|
||||
void HistoryInner::applyDragSelection(
|
||||
not_null<SelectedItems*> toItems) const {
|
||||
const auto selfromy = itemTop(_dragSelFrom);
|
||||
const auto seltoy = [&] {
|
||||
auto result = itemTop(_dragSelTo);
|
||||
return (result < 0) ? result : (result + _dragSelTo->height());
|
||||
}();
|
||||
if (selfromy < 0 || seltoy < 0) {
|
||||
return;
|
||||
}
|
||||
seltoy += _dragSelTo->height();
|
||||
|
||||
if (!toItems->empty() && toItems->cbegin()->second != FullSelection) {
|
||||
toItems->clear();
|
||||
}
|
||||
if (_dragSelecting) {
|
||||
int32 fromblock = _dragSelFrom->block()->indexInHistory(), fromitem = _dragSelFrom->indexInBlock();
|
||||
int32 toblock = _dragSelTo->block()->indexInHistory(), toitem = _dragSelTo->indexInBlock();
|
||||
auto fromblock = _dragSelFrom->block()->indexInHistory();
|
||||
auto fromitem = _dragSelFrom->indexInBlock();
|
||||
auto toblock = _dragSelTo->block()->indexInHistory();
|
||||
auto toitem = _dragSelTo->indexInBlock();
|
||||
if (_migrated) {
|
||||
if (_dragSelFrom->history() == _migrated) {
|
||||
if (_dragSelTo->history() == _migrated) {
|
||||
addSelectionRange(toItems, fromblock, fromitem, toblock, toitem, _migrated);
|
||||
addSelectionRange(toItems, _migrated, fromblock, fromitem, toblock, toitem);
|
||||
toblock = -1;
|
||||
toitem = -1;
|
||||
} else {
|
||||
addSelectionRange(toItems, fromblock, fromitem, _migrated->blocks.size() - 1, _migrated->blocks.back()->items.size() - 1, _migrated);
|
||||
addSelectionRange(toItems, _migrated, fromblock, fromitem, _migrated->blocks.size() - 1, _migrated->blocks.back()->items.size() - 1);
|
||||
}
|
||||
fromblock = 0;
|
||||
fromitem = 0;
|
||||
@ -2425,20 +2589,20 @@ void HistoryInner::applyDragSelection(SelectedItems *toItems) const {
|
||||
toitem = -1;
|
||||
}
|
||||
}
|
||||
addSelectionRange(toItems, fromblock, fromitem, toblock, toitem, _history);
|
||||
addSelectionRange(toItems, _history, fromblock, fromitem, toblock, toitem);
|
||||
} else {
|
||||
for (auto i = toItems->begin(); i != toItems->cend();) {
|
||||
auto toRemove = std::vector<not_null<HistoryItem*>>();
|
||||
for (auto i = toItems->begin(); i != toItems->cend(); ++i) {
|
||||
auto iy = itemTop(i->first);
|
||||
if (iy < 0) {
|
||||
if (iy < -1) i = toItems->erase(i);
|
||||
continue;
|
||||
}
|
||||
if (iy >= selfromy && iy < seltoy) {
|
||||
i = toItems->erase(i);
|
||||
} else {
|
||||
++i;
|
||||
if (iy < -1) {
|
||||
toRemove.push_back(i->first);
|
||||
} else if (iy >= 0 && iy >= selfromy && iy < seltoy) {
|
||||
toRemove.push_back(i->first);
|
||||
}
|
||||
}
|
||||
for (const auto item : toRemove) {
|
||||
changeDragSelection(toItems, item, SelectAction::Deselect);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -66,7 +66,7 @@ public:
|
||||
HistoryTopBarWidget::SelectedState getSelectionState() const;
|
||||
void clearSelectedItems(bool onlyTextSelection = false);
|
||||
MessageIdsList getSelectedItems() const;
|
||||
void selectItem(HistoryItem *item);
|
||||
void selectItem(not_null<HistoryItem*> item);
|
||||
|
||||
void updateBotInfo(bool recount = true);
|
||||
|
||||
@ -165,6 +165,10 @@ private:
|
||||
HistoryItem *prevItem(HistoryItem *item);
|
||||
HistoryItem *nextItem(HistoryItem *item);
|
||||
void updateDragSelection(HistoryItem *dragSelFrom, HistoryItem *dragSelTo, bool dragSelecting);
|
||||
TextSelection itemRenderSelection(
|
||||
not_null<HistoryItem*> item,
|
||||
int selfromy,
|
||||
int seltoy) const;
|
||||
|
||||
void setToClipboard(const TextWithEntities &forClipboard, QClipboard::Mode mode = QClipboard::Clipboard);
|
||||
|
||||
@ -217,8 +221,26 @@ private:
|
||||
using SelectedItems = std::map<HistoryItem*, TextSelection, std::less<>>;
|
||||
SelectedItems _selected;
|
||||
void applyDragSelection();
|
||||
void applyDragSelection(SelectedItems *toItems) const;
|
||||
void addSelectionRange(SelectedItems *toItems, int32 fromblock, int32 fromitem, int32 toblock, int32 toitem, History *h) const;
|
||||
void applyDragSelection(not_null<SelectedItems*> toItems) const;
|
||||
void addSelectionRange(
|
||||
not_null<SelectedItems*> toItems,
|
||||
not_null<History*> history,
|
||||
int fromblock,
|
||||
int fromitem,
|
||||
int toblock,
|
||||
int toitem) const;
|
||||
bool isFullSelected(
|
||||
not_null<SelectedItems*> toItems,
|
||||
not_null<HistoryItem*> item) const;
|
||||
enum class SelectAction {
|
||||
Select,
|
||||
Deselect,
|
||||
Invert,
|
||||
};
|
||||
void changeDragSelection(
|
||||
not_null<SelectedItems*> toItems,
|
||||
not_null<HistoryItem*> item,
|
||||
SelectAction action) const;
|
||||
|
||||
// Does any of the shown histories has this flag set.
|
||||
bool hasPendingResizedItems() const {
|
||||
@ -230,6 +252,7 @@ private:
|
||||
QPoint _dragStartPosition;
|
||||
QPoint _mousePosition;
|
||||
HistoryItem *_mouseActionItem = nullptr;
|
||||
HistoryItem *_dragStateItem = nullptr;
|
||||
HistoryCursorState _mouseCursorState = HistoryDefaultCursorState;
|
||||
uint16 _mouseTextSymbol = 0;
|
||||
bool _pressWasInactive = false;
|
||||
|
@ -46,6 +46,29 @@ constexpr int kAttachMessageToPreviousSecondsDelta = 900;
|
||||
|
||||
} // namespace
|
||||
|
||||
HistoryTextState::HistoryTextState(not_null<const HistoryItem*> item)
|
||||
: itemId(item->fullId()) {
|
||||
}
|
||||
|
||||
HistoryTextState::HistoryTextState(
|
||||
not_null<const HistoryItem*> item,
|
||||
const Text::StateResult &state)
|
||||
: itemId(item->fullId())
|
||||
, cursor(state.uponSymbol
|
||||
? HistoryInTextCursorState
|
||||
: HistoryDefaultCursorState)
|
||||
, link(state.link)
|
||||
, afterSymbol(state.afterSymbol)
|
||||
, symbol(state.symbol) {
|
||||
}
|
||||
|
||||
HistoryTextState::HistoryTextState(
|
||||
not_null<const HistoryItem*> item,
|
||||
ClickHandlerPtr link)
|
||||
: itemId(item->fullId())
|
||||
, link(link) {
|
||||
}
|
||||
|
||||
ReplyMarkupClickHandler::ReplyMarkupClickHandler(const HistoryItem *item, int row, int col)
|
||||
: _itemId(item->fullId())
|
||||
, _row(row)
|
||||
|
@ -79,25 +79,33 @@ enum HistoryCursorState {
|
||||
|
||||
struct HistoryTextState {
|
||||
HistoryTextState() = default;
|
||||
HistoryTextState(const Text::StateResult &state)
|
||||
: cursor(state.uponSymbol ? HistoryInTextCursorState : HistoryDefaultCursorState)
|
||||
, link(state.link)
|
||||
, afterSymbol(state.afterSymbol)
|
||||
, symbol(state.symbol) {
|
||||
HistoryTextState(not_null<const HistoryItem*> item);
|
||||
HistoryTextState(
|
||||
not_null<const HistoryItem*> item,
|
||||
const Text::StateResult &state);
|
||||
HistoryTextState(
|
||||
not_null<const HistoryItem*> item,
|
||||
ClickHandlerPtr link);
|
||||
HistoryTextState(
|
||||
std::nullptr_t,
|
||||
const Text::StateResult &state)
|
||||
: cursor(state.uponSymbol
|
||||
? HistoryInTextCursorState
|
||||
: HistoryDefaultCursorState)
|
||||
, link(state.link)
|
||||
, afterSymbol(state.afterSymbol)
|
||||
, symbol(state.symbol) {
|
||||
}
|
||||
HistoryTextState &operator=(const Text::StateResult &state) {
|
||||
cursor = state.uponSymbol ? HistoryInTextCursorState : HistoryDefaultCursorState;
|
||||
link = state.link;
|
||||
afterSymbol = state.afterSymbol;
|
||||
symbol = state.symbol;
|
||||
return *this;
|
||||
}
|
||||
HistoryTextState(ClickHandlerPtr link) : link(link) {
|
||||
HistoryTextState(std::nullptr_t, ClickHandlerPtr link)
|
||||
: link(link) {
|
||||
}
|
||||
|
||||
FullMsgId itemId;
|
||||
HistoryCursorState cursor = HistoryDefaultCursorState;
|
||||
ClickHandlerPtr link;
|
||||
bool afterSymbol = false;
|
||||
uint16 symbol = 0;
|
||||
|
||||
};
|
||||
|
||||
struct HistoryStateRequest {
|
||||
|
@ -135,7 +135,8 @@ public:
|
||||
return false;
|
||||
}
|
||||
virtual std::unique_ptr<HistoryMedia> clone(
|
||||
not_null<HistoryItem*> newParent) const = 0;
|
||||
not_null<HistoryItem*> newParent,
|
||||
not_null<HistoryItem*> realParent) const = 0;
|
||||
|
||||
virtual DocumentData *getDocument() {
|
||||
return nullptr;
|
||||
|
@ -54,13 +54,20 @@ HistoryGroupedMedia::Element::Element(not_null<HistoryItem*> item)
|
||||
HistoryGroupedMedia::HistoryGroupedMedia(
|
||||
not_null<HistoryItem*> parent,
|
||||
const std::vector<not_null<HistoryItem*>> &others)
|
||||
: HistoryMedia(parent) {
|
||||
: HistoryMedia(parent)
|
||||
, _caption(st::minPhotoSize - st::msgPadding.left() - st::msgPadding.right()) {
|
||||
const auto result = applyGroup(others);
|
||||
|
||||
Ensures(result);
|
||||
}
|
||||
|
||||
void HistoryGroupedMedia::initDimensions() {
|
||||
if (_caption.hasSkipBlock()) {
|
||||
_caption.setSkipBlock(
|
||||
_parent->skipBlockWidth(),
|
||||
_parent->skipBlockHeight());
|
||||
}
|
||||
|
||||
std::vector<QSize> sizes;
|
||||
sizes.reserve(_elements.size());
|
||||
for (const auto &element : _elements) {
|
||||
@ -84,6 +91,14 @@ void HistoryGroupedMedia::initDimensions() {
|
||||
_elements[i].initialGeometry = item.geometry;
|
||||
_elements[i].sides = item.sides;
|
||||
}
|
||||
|
||||
if (!_caption.isEmpty()) {
|
||||
auto captionw = _maxw - st::msgPadding.left() - st::msgPadding.right();
|
||||
_minh += st::mediaCaptionSkip + _caption.countHeight(captionw);
|
||||
if (isBubbleBottom()) {
|
||||
_minh += st::msgPadding.bottom();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int HistoryGroupedMedia::resizeGetHeight(int width) {
|
||||
@ -94,7 +109,7 @@ int HistoryGroupedMedia::resizeGetHeight(int width) {
|
||||
}
|
||||
|
||||
const auto initialSpacing = st::historyGroupSkip;
|
||||
const auto factor = width / float64(st::historyGroupWidthMax);
|
||||
const auto factor = width / float64(_maxw);
|
||||
const auto scale = [&](int value) {
|
||||
return int(std::round(value * factor));
|
||||
};
|
||||
@ -124,6 +139,15 @@ int HistoryGroupedMedia::resizeGetHeight(int width) {
|
||||
|
||||
accumulate_max(_height, top + height);
|
||||
}
|
||||
|
||||
if (!_caption.isEmpty()) {
|
||||
const auto captionw = _width - st::msgPadding.left() - st::msgPadding.right();
|
||||
_height += st::mediaPadding.bottom() + st::mediaCaptionSkip + _caption.countHeight(captionw);
|
||||
if (isBubbleBottom()) {
|
||||
_height += st::msgPadding.bottom();
|
||||
}
|
||||
}
|
||||
|
||||
return _height;
|
||||
}
|
||||
|
||||
@ -132,7 +156,13 @@ void HistoryGroupedMedia::draw(
|
||||
const QRect &clip,
|
||||
TextSelection selection,
|
||||
TimeMs ms) const {
|
||||
for (const auto &element : _elements) {
|
||||
for (auto i = 0, count = int(_elements.size()); i != count; ++i) {
|
||||
const auto &element = _elements[i];
|
||||
const auto elementSelection = (selection == FullSelection)
|
||||
? FullSelection
|
||||
: IsGroupItemSelection(selection, i)
|
||||
? FullSelection
|
||||
: TextSelection();
|
||||
auto corners = GetCornersFromSides(element.sides);
|
||||
if (!isBubbleTop()) {
|
||||
corners &= ~(RectPart::TopLeft | RectPart::TopRight);
|
||||
@ -143,13 +173,36 @@ void HistoryGroupedMedia::draw(
|
||||
element.content->drawGrouped(
|
||||
p,
|
||||
clip,
|
||||
selection,
|
||||
elementSelection,
|
||||
ms,
|
||||
element.geometry,
|
||||
corners,
|
||||
&element.cacheKey,
|
||||
&element.cache);
|
||||
}
|
||||
|
||||
// date
|
||||
const auto selected = (selection == FullSelection);
|
||||
if (!_caption.isEmpty()) {
|
||||
const auto captionw = _width - st::msgPadding.left() - st::msgPadding.right();
|
||||
const auto outbg = _parent->hasOutLayout();
|
||||
const auto captiony = _height
|
||||
- (isBubbleBottom() ? st::msgPadding.bottom() : 0)
|
||||
- _caption.countHeight(captionw);
|
||||
p.setPen(outbg ? (selected ? st::historyTextOutFgSelected : st::historyTextOutFg) : (selected ? st::historyTextInFgSelected : st::historyTextInFg));
|
||||
_caption.draw(p, st::msgPadding.left(), captiony, captionw, style::al_left, 0, -1, selection);
|
||||
} else if (_parent->getMedia() == this) {
|
||||
auto fullRight = _width;
|
||||
auto fullBottom = _height;
|
||||
if (_parent->id < 0 || App::hoveredItem() == _parent) {
|
||||
_parent->drawInfo(p, fullRight, fullBottom, _width, selected, InfoDisplayOverImage);
|
||||
}
|
||||
if (!_parent->hasBubble() && _parent->displayRightAction()) {
|
||||
auto fastShareLeft = (fullRight + st::historyFastShareLeft);
|
||||
auto fastShareTop = (fullBottom - st::historyFastShareBottom - st::historyFastShareSize);
|
||||
_parent->drawRightAction(p, fastShareLeft, fastShareTop, _width);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HistoryTextState HistoryGroupedMedia::getState(
|
||||
@ -157,10 +210,39 @@ HistoryTextState HistoryGroupedMedia::getState(
|
||||
HistoryStateRequest request) const {
|
||||
for (const auto &element : _elements) {
|
||||
if (element.geometry.contains(point)) {
|
||||
return element.content->getStateGrouped(
|
||||
auto result = element.content->getStateGrouped(
|
||||
element.geometry,
|
||||
point,
|
||||
request);
|
||||
result.itemId = element.item->fullId();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
if (!_caption.isEmpty()) {
|
||||
const auto captionw = _width - st::msgPadding.left() - st::msgPadding.right();
|
||||
const auto captiony = _height
|
||||
- (isBubbleBottom() ? st::msgPadding.bottom() : 0)
|
||||
- _caption.countHeight(captionw);
|
||||
if (QRect(st::msgPadding.left(), captiony, captionw, _height - captiony).contains(point)) {
|
||||
return HistoryTextState(_parent, _caption.getState(
|
||||
point - QPoint(st::msgPadding.left(), captiony),
|
||||
captionw,
|
||||
request.forText()));
|
||||
}
|
||||
}
|
||||
auto result = HistoryTextState(_parent);
|
||||
if (_caption.isEmpty() && _parent->getMedia() == this) {
|
||||
auto fullRight = _width;
|
||||
auto fullBottom = _height;
|
||||
if (_parent->pointInTime(fullRight, fullBottom, point, InfoDisplayOverImage)) {
|
||||
result.cursor = HistoryInDateCursorState;
|
||||
}
|
||||
if (!_parent->hasBubble() && _parent->displayRightAction()) {
|
||||
auto fastShareLeft = (fullRight + st::historyFastShareLeft);
|
||||
auto fastShareTop = (fullBottom - st::historyFastShareBottom - st::historyFastShareSize);
|
||||
if (QRect(fastShareLeft, fastShareTop, st::historyFastShareSize, st::historyFastShareSize).contains(point)) {
|
||||
result.link = _parent->rightActionLink();
|
||||
}
|
||||
}
|
||||
}
|
||||
return HistoryTextState();
|
||||
@ -191,6 +273,14 @@ TextSelection HistoryGroupedMedia::adjustSelection(
|
||||
return _caption.adjustSelection(selection, type);
|
||||
}
|
||||
|
||||
QString HistoryGroupedMedia::notificationText() const {
|
||||
return WithCaptionNotificationText(lang(lng_in_dlg_album), _caption);
|
||||
}
|
||||
|
||||
QString HistoryGroupedMedia::inDialogsText() const {
|
||||
return WithCaptionDialogsText(lang(lng_in_dlg_album), _caption);
|
||||
}
|
||||
|
||||
TextWithEntities HistoryGroupedMedia::selectedText(
|
||||
TextSelection selection) const {
|
||||
return WithCaptionSelectedText(
|
||||
@ -212,6 +302,9 @@ void HistoryGroupedMedia::clickHandlerPressedChanged(
|
||||
bool pressed) {
|
||||
for (const auto &element : _elements) {
|
||||
element.content->clickHandlerPressedChanged(p, pressed);
|
||||
if (pressed && element.content->dragItemByHandler(p)) {
|
||||
App::pressedLinkItem(element.item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -243,7 +336,7 @@ bool HistoryGroupedMedia::applyGroup(
|
||||
Assert(media != nullptr && media->canBeGrouped());
|
||||
|
||||
_elements.push_back(Element(item));
|
||||
_elements.back().content = item->getMedia()->clone(_parent);
|
||||
_elements.back().content = item->getMedia()->clone(_parent, item);
|
||||
};
|
||||
if (_elements.empty()) {
|
||||
pushElement(_parent);
|
||||
|
@ -34,7 +34,8 @@ public:
|
||||
return MediaTypeGrouped;
|
||||
}
|
||||
std::unique_ptr<HistoryMedia> clone(
|
||||
not_null<HistoryItem*> newParent) const override {
|
||||
not_null<HistoryItem*> newParent,
|
||||
not_null<HistoryItem*> realParent) const override {
|
||||
Unexpected("Clone HistoryGroupedMedia.");
|
||||
}
|
||||
|
||||
@ -64,6 +65,8 @@ public:
|
||||
return !_caption.isEmpty();
|
||||
}
|
||||
|
||||
QString notificationText() const override;
|
||||
QString inDialogsText() const override;
|
||||
TextWithEntities selectedText(TextSelection selection) const override;
|
||||
|
||||
void clickHandlerActiveChanged(
|
||||
|
@ -272,12 +272,16 @@ HistoryPhoto::HistoryPhoto(
|
||||
: HistoryFileMedia(parent)
|
||||
, _data(photo)
|
||||
, _caption(st::minPhotoSize - st::msgPadding.left() - st::msgPadding.right()) {
|
||||
const auto fullId = parent->fullId();
|
||||
setLinks(
|
||||
MakeShared<PhotoOpenClickHandler>(_data),
|
||||
MakeShared<PhotoSaveClickHandler>(_data),
|
||||
MakeShared<PhotoCancelClickHandler>(_data));
|
||||
MakeShared<PhotoOpenClickHandler>(_data, fullId),
|
||||
MakeShared<PhotoSaveClickHandler>(_data, fullId),
|
||||
MakeShared<PhotoCancelClickHandler>(_data, fullId));
|
||||
if (!caption.isEmpty()) {
|
||||
_caption.setText(st::messageTextStyle, caption + _parent->skipBlock(), itemTextNoMonoOptions(_parent));
|
||||
_caption.setText(
|
||||
st::messageTextStyle,
|
||||
caption + _parent->skipBlock(),
|
||||
itemTextNoMonoOptions(_parent));
|
||||
}
|
||||
init();
|
||||
}
|
||||
@ -289,10 +293,11 @@ HistoryPhoto::HistoryPhoto(
|
||||
int width)
|
||||
: HistoryFileMedia(parent)
|
||||
, _data(photo) {
|
||||
const auto fullId = parent->fullId();
|
||||
setLinks(
|
||||
MakeShared<PhotoOpenClickHandler>(_data, chat),
|
||||
MakeShared<PhotoSaveClickHandler>(_data, chat),
|
||||
MakeShared<PhotoCancelClickHandler>(_data, chat));
|
||||
MakeShared<PhotoOpenClickHandler>(_data, fullId, chat),
|
||||
MakeShared<PhotoSaveClickHandler>(_data, fullId, chat),
|
||||
MakeShared<PhotoCancelClickHandler>(_data, fullId, chat));
|
||||
|
||||
_width = width;
|
||||
init();
|
||||
@ -308,16 +313,18 @@ HistoryPhoto::HistoryPhoto(
|
||||
|
||||
HistoryPhoto::HistoryPhoto(
|
||||
not_null<HistoryItem*> parent,
|
||||
not_null<HistoryItem*> realParent,
|
||||
const HistoryPhoto &other)
|
||||
: HistoryFileMedia(parent)
|
||||
, _data(other._data)
|
||||
, _pixw(other._pixw)
|
||||
, _pixh(other._pixh)
|
||||
, _caption(other._caption) {
|
||||
const auto fullId = realParent->fullId();
|
||||
setLinks(
|
||||
MakeShared<PhotoOpenClickHandler>(_data),
|
||||
MakeShared<PhotoSaveClickHandler>(_data),
|
||||
MakeShared<PhotoCancelClickHandler>(_data));
|
||||
MakeShared<PhotoOpenClickHandler>(_data, fullId),
|
||||
MakeShared<PhotoSaveClickHandler>(_data, fullId),
|
||||
MakeShared<PhotoCancelClickHandler>(_data, fullId));
|
||||
|
||||
init();
|
||||
}
|
||||
@ -533,9 +540,11 @@ void HistoryPhoto::draw(Painter &p, const QRect &r, TextSelection selection, Tim
|
||||
}
|
||||
|
||||
HistoryTextState HistoryPhoto::getState(QPoint point, HistoryStateRequest request) const {
|
||||
HistoryTextState result;
|
||||
auto result = HistoryTextState(_parent);
|
||||
|
||||
if (_width < st::msgPadding.left() + st::msgPadding.right() + 1) return result;
|
||||
if (_width < st::msgPadding.left() + st::msgPadding.right() + 1) {
|
||||
return result;
|
||||
}
|
||||
int skipx = 0, skipy = 0, width = _width, height = _height;
|
||||
auto bubble = _parent->hasBubble();
|
||||
|
||||
@ -549,7 +558,10 @@ HistoryTextState HistoryPhoto::getState(QPoint point, HistoryStateRequest reques
|
||||
height -= st::msgPadding.bottom();
|
||||
}
|
||||
if (QRect(st::msgPadding.left(), height, captionw, _height - height).contains(point)) {
|
||||
result = _caption.getState(point - QPoint(st::msgPadding.left(), height), captionw, request.forText());
|
||||
result = HistoryTextState(_parent, _caption.getState(
|
||||
point - QPoint(st::msgPadding.left(), height),
|
||||
captionw,
|
||||
request.forText()));
|
||||
return result;
|
||||
}
|
||||
height -= st::mediaCaptionSkip;
|
||||
@ -696,7 +708,7 @@ HistoryTextState HistoryPhoto::getStateGrouped(
|
||||
return {};
|
||||
}
|
||||
const auto delayed = _data->full->toDelayedStorageImage();
|
||||
return _data->uploading()
|
||||
return HistoryTextState(_parent, _data->uploading()
|
||||
? _cancell
|
||||
: _data->loaded()
|
||||
? _openl
|
||||
@ -704,7 +716,7 @@ HistoryTextState HistoryPhoto::getStateGrouped(
|
||||
? ((!delayed || !delayed->location().isNull())
|
||||
? _cancell
|
||||
: ClickHandlerPtr())
|
||||
: _savel;
|
||||
: _savel);
|
||||
}
|
||||
|
||||
void HistoryPhoto::validateGroupedCache(
|
||||
@ -872,10 +884,13 @@ HistoryVideo::HistoryVideo(
|
||||
, _thumbw(1)
|
||||
, _caption(st::minPhotoSize - st::msgPadding.left() - st::msgPadding.right()) {
|
||||
if (!caption.isEmpty()) {
|
||||
_caption.setText(st::messageTextStyle, caption + _parent->skipBlock(), itemTextNoMonoOptions(_parent));
|
||||
_caption.setText(
|
||||
st::messageTextStyle,
|
||||
caption + _parent->skipBlock(),
|
||||
itemTextNoMonoOptions(_parent));
|
||||
}
|
||||
|
||||
setDocumentLinks(_data);
|
||||
setDocumentLinks(_data, parent);
|
||||
|
||||
setStatusSize(FileStatusSizeReady);
|
||||
|
||||
@ -884,12 +899,13 @@ HistoryVideo::HistoryVideo(
|
||||
|
||||
HistoryVideo::HistoryVideo(
|
||||
not_null<HistoryItem*> parent,
|
||||
not_null<HistoryItem*> realParent,
|
||||
const HistoryVideo &other)
|
||||
: HistoryFileMedia(parent)
|
||||
, _data(other._data)
|
||||
, _thumbw(other._thumbw)
|
||||
, _caption(other._caption) {
|
||||
setDocumentLinks(_data);
|
||||
setDocumentLinks(_data, realParent);
|
||||
|
||||
setStatusSize(other._statusSize);
|
||||
}
|
||||
@ -1089,7 +1105,7 @@ HistoryTextState HistoryVideo::getState(QPoint point, HistoryStateRequest reques
|
||||
return {};
|
||||
}
|
||||
|
||||
HistoryTextState result;
|
||||
auto result = HistoryTextState(_parent);
|
||||
bool loaded = _data->loaded();
|
||||
|
||||
int32 skipx = 0, skipy = 0, width = _width, height = _height;
|
||||
@ -1105,7 +1121,10 @@ HistoryTextState HistoryVideo::getState(QPoint point, HistoryStateRequest reques
|
||||
height -= st::msgPadding.bottom();
|
||||
}
|
||||
if (QRect(st::msgPadding.left(), height, captionw, _height - height).contains(point)) {
|
||||
result = _caption.getState(point - QPoint(st::msgPadding.left(), height), captionw, request.forText());
|
||||
result = HistoryTextState(_parent, _caption.getState(
|
||||
point - QPoint(st::msgPadding.left(), height),
|
||||
captionw,
|
||||
request.forText()));
|
||||
}
|
||||
height -= st::mediaCaptionSkip;
|
||||
}
|
||||
@ -1242,13 +1261,13 @@ HistoryTextState HistoryVideo::getStateGrouped(
|
||||
if (!geometry.contains(point)) {
|
||||
return {};
|
||||
}
|
||||
return _data->uploading()
|
||||
return HistoryTextState(_parent, _data->uploading()
|
||||
? _cancell
|
||||
: _data->loaded()
|
||||
? _openl
|
||||
: _data->loading()
|
||||
? _cancell
|
||||
: _savel;
|
||||
: _savel);
|
||||
}
|
||||
|
||||
void HistoryVideo::validateGroupedCache(
|
||||
@ -1425,7 +1444,7 @@ HistoryDocument::HistoryDocument(
|
||||
fillNamedFromData(named);
|
||||
}
|
||||
|
||||
setDocumentLinks(_data);
|
||||
setDocumentLinks(_data, parent);
|
||||
|
||||
setStatusSize(FileStatusSizeReady);
|
||||
|
||||
@ -1450,7 +1469,7 @@ HistoryDocument::HistoryDocument(
|
||||
}
|
||||
}
|
||||
|
||||
setDocumentLinks(_data);
|
||||
setDocumentLinks(_data, parent);
|
||||
|
||||
setStatusSize(other._statusSize);
|
||||
|
||||
@ -1821,9 +1840,11 @@ void HistoryDocument::draw(Painter &p, const QRect &r, TextSelection selection,
|
||||
}
|
||||
|
||||
HistoryTextState HistoryDocument::getState(QPoint point, HistoryStateRequest request) const {
|
||||
HistoryTextState result;
|
||||
auto result = HistoryTextState(_parent);
|
||||
|
||||
if (_width < st::msgPadding.left() + st::msgPadding.right() + 1) return result;
|
||||
if (_width < st::msgPadding.left() + st::msgPadding.right() + 1) {
|
||||
return result;
|
||||
}
|
||||
|
||||
bool loaded = _data->loaded();
|
||||
|
||||
@ -1882,7 +1903,10 @@ HistoryTextState HistoryDocument::getState(QPoint point, HistoryStateRequest req
|
||||
auto height = _height;
|
||||
if (auto captioned = Get<HistoryDocumentCaptioned>()) {
|
||||
if (point.y() >= bottom) {
|
||||
result = captioned->_caption.getState(point - QPoint(st::msgPadding.left(), bottom), _width - st::msgPadding.left() - st::msgPadding.right(), request.forText());
|
||||
result = HistoryTextState(_parent, captioned->_caption.getState(
|
||||
point - QPoint(st::msgPadding.left(), bottom),
|
||||
_width - st::msgPadding.left() - st::msgPadding.right(),
|
||||
request.forText()));
|
||||
return result;
|
||||
}
|
||||
auto captionw = _width - st::msgPadding.left() - st::msgPadding.right();
|
||||
@ -2163,7 +2187,7 @@ HistoryGif::HistoryGif(
|
||||
: HistoryFileMedia(parent)
|
||||
, _data(document)
|
||||
, _caption(st::minPhotoSize - st::msgPadding.left() - st::msgPadding.right()) {
|
||||
setDocumentLinks(_data, true);
|
||||
setDocumentLinks(_data, parent, true);
|
||||
|
||||
setStatusSize(FileStatusSizeReady);
|
||||
|
||||
@ -2182,7 +2206,7 @@ HistoryGif::HistoryGif(
|
||||
, _thumbw(other._thumbw)
|
||||
, _thumbh(other._thumbh)
|
||||
, _caption(other._caption) {
|
||||
setDocumentLinks(_data, true);
|
||||
setDocumentLinks(_data, parent, true);
|
||||
|
||||
setStatusSize(other._statusSize);
|
||||
}
|
||||
@ -2614,9 +2638,11 @@ void HistoryGif::draw(Painter &p, const QRect &r, TextSelection selection, TimeM
|
||||
}
|
||||
|
||||
HistoryTextState HistoryGif::getState(QPoint point, HistoryStateRequest request) const {
|
||||
HistoryTextState result;
|
||||
auto result = HistoryTextState(_parent);
|
||||
|
||||
if (_width < st::msgPadding.left() + st::msgPadding.right() + 1) return result;
|
||||
if (_width < st::msgPadding.left() + st::msgPadding.right() + 1) {
|
||||
return result;
|
||||
}
|
||||
int32 skipx = 0, skipy = 0, width = _width, height = _height;
|
||||
bool bubble = _parent->hasBubble();
|
||||
|
||||
@ -2630,7 +2656,10 @@ HistoryTextState HistoryGif::getState(QPoint point, HistoryStateRequest request)
|
||||
height -= st::msgPadding.bottom();
|
||||
}
|
||||
if (QRect(st::msgPadding.left(), height, captionw, _height - height).contains(point)) {
|
||||
result = _caption.getState(point - QPoint(st::msgPadding.left(), height), captionw, request.forText());
|
||||
result = HistoryTextState(_parent, _caption.getState(
|
||||
point - QPoint(st::msgPadding.left(), height),
|
||||
captionw,
|
||||
request.forText()));
|
||||
return result;
|
||||
}
|
||||
height -= st::mediaCaptionSkip;
|
||||
@ -2679,7 +2708,10 @@ HistoryTextState HistoryGif::getState(QPoint point, HistoryStateRequest request)
|
||||
if (breakEverywhere) {
|
||||
textRequest.flags |= Text::StateRequest::Flag::BreakEverywhere;
|
||||
}
|
||||
result = forwarded->_text.getState(point - QPoint(rectx + st::msgReplyPadding.left(), recty + st::msgReplyPadding.top()), innerw, textRequest);
|
||||
result = HistoryTextState(_parent, forwarded->_text.getState(
|
||||
point - QPoint(rectx + st::msgReplyPadding.left(), recty + st::msgReplyPadding.top()),
|
||||
innerw,
|
||||
textRequest));
|
||||
result.symbol = 0;
|
||||
result.afterSymbol = false;
|
||||
if (breakEverywhere) {
|
||||
@ -3138,8 +3170,10 @@ void HistorySticker::draw(Painter &p, const QRect &r, TextSelection selection, T
|
||||
}
|
||||
|
||||
HistoryTextState HistorySticker::getState(QPoint point, HistoryStateRequest request) const {
|
||||
HistoryTextState result;
|
||||
if (_width < st::msgPadding.left() + st::msgPadding.right() + 1) return result;
|
||||
auto result = HistoryTextState(_parent);
|
||||
if (_width < st::msgPadding.left() + st::msgPadding.right() + 1) {
|
||||
return result;
|
||||
}
|
||||
|
||||
auto outbg = _parent->hasOutLayout();
|
||||
auto childmedia = (_parent->getMedia() != this);
|
||||
@ -3408,7 +3442,7 @@ void HistoryContact::draw(Painter &p, const QRect &r, TextSelection selection, T
|
||||
}
|
||||
|
||||
HistoryTextState HistoryContact::getState(QPoint point, HistoryStateRequest request) const {
|
||||
HistoryTextState result;
|
||||
auto result = HistoryTextState(_parent);
|
||||
|
||||
int32 nameleft = 0, nametop = 0, nameright = 0, statustop = 0, linktop = 0;
|
||||
auto topMinus = isBubbleTop() ? 0 : st::msgFileTopMinus;
|
||||
@ -3556,7 +3590,7 @@ void HistoryCall::draw(Painter &p, const QRect &r, TextSelection selection, Time
|
||||
}
|
||||
|
||||
HistoryTextState HistoryCall::getState(QPoint point, HistoryStateRequest request) const {
|
||||
HistoryTextState result;
|
||||
auto result = HistoryTextState(_parent);
|
||||
if (QRect(0, 0, _width, _height).contains(point)) {
|
||||
result.link = _link;
|
||||
return result;
|
||||
@ -3617,9 +3651,12 @@ HistoryWebPage::HistoryWebPage(not_null<HistoryItem*> parent, not_null<WebPageDa
|
||||
, _description(st::msgMinWidth - st::webPageLeft) {
|
||||
}
|
||||
|
||||
HistoryWebPage::HistoryWebPage(not_null<HistoryItem*> parent, const HistoryWebPage &other) : HistoryMedia(parent)
|
||||
HistoryWebPage::HistoryWebPage(
|
||||
not_null<HistoryItem*> parent,
|
||||
const HistoryWebPage &other)
|
||||
: HistoryMedia(parent)
|
||||
, _data(other._data)
|
||||
, _attach(other._attach ? other._attach->clone(parent) : nullptr)
|
||||
, _attach(other._attach ? other._attach->clone(parent, parent) : nullptr)
|
||||
, _asArticle(other._asArticle)
|
||||
, _title(other._title)
|
||||
, _description(other._description)
|
||||
@ -3999,9 +4036,11 @@ void HistoryWebPage::draw(Painter &p, const QRect &r, TextSelection selection, T
|
||||
}
|
||||
|
||||
HistoryTextState HistoryWebPage::getState(QPoint point, HistoryStateRequest request) const {
|
||||
HistoryTextState result;
|
||||
auto result = HistoryTextState(_parent);
|
||||
|
||||
if (_width < st::msgPadding.left() + st::msgPadding.right() + 1) return result;
|
||||
if (_width < st::msgPadding.left() + st::msgPadding.right() + 1) {
|
||||
return result;
|
||||
}
|
||||
int32 skipx = 0, skipy = 0, width = _width, height = _height;
|
||||
|
||||
QMargins bubble(_attach ? _attach->bubbleMargins() : QMargins());
|
||||
@ -4030,7 +4069,11 @@ HistoryTextState HistoryWebPage::getState(QPoint point, HistoryStateRequest requ
|
||||
if (point.y() >= tshift && point.y() < tshift + _titleLines * lineHeight) {
|
||||
Text::StateRequestElided titleRequest = request.forText();
|
||||
titleRequest.lines = _titleLines;
|
||||
result = _title.getStateElidedLeft(point - QPoint(padding.left(), tshift), width, _width, titleRequest);
|
||||
result = HistoryTextState(_parent, _title.getStateElidedLeft(
|
||||
point - QPoint(padding.left(), tshift),
|
||||
width,
|
||||
_width,
|
||||
titleRequest));
|
||||
} else if (point.y() >= tshift + _titleLines * lineHeight) {
|
||||
symbolAdd += _title.length();
|
||||
}
|
||||
@ -4042,9 +4085,17 @@ HistoryTextState HistoryWebPage::getState(QPoint point, HistoryStateRequest requ
|
||||
if (_descriptionLines > 0) {
|
||||
Text::StateRequestElided descriptionRequest = request.forText();
|
||||
descriptionRequest.lines = _descriptionLines;
|
||||
result = _description.getStateElidedLeft(point - QPoint(padding.left(), tshift), width, _width, descriptionRequest);
|
||||
result = HistoryTextState(_parent, _description.getStateElidedLeft(
|
||||
point - QPoint(padding.left(), tshift),
|
||||
width,
|
||||
_width,
|
||||
descriptionRequest));
|
||||
} else {
|
||||
result = _description.getStateLeft(point - QPoint(padding.left(), tshift), width, _width, request.forText());
|
||||
result = HistoryTextState(_parent, _description.getStateLeft(
|
||||
point - QPoint(padding.left(), tshift),
|
||||
width,
|
||||
_width,
|
||||
request.forText()));
|
||||
}
|
||||
} else if (point.y() >= tshift + descriptionHeight) {
|
||||
symbolAdd += _description.length();
|
||||
@ -4178,7 +4229,7 @@ HistoryGame::HistoryGame(
|
||||
const HistoryGame &other)
|
||||
: HistoryMedia(parent)
|
||||
, _data(other._data)
|
||||
, _attach(other._attach ? other._attach->clone(parent) : nullptr)
|
||||
, _attach(other._attach ? other._attach->clone(parent, parent) : nullptr)
|
||||
, _title(other._title)
|
||||
, _description(other._description) {
|
||||
}
|
||||
@ -4395,9 +4446,11 @@ void HistoryGame::draw(Painter &p, const QRect &r, TextSelection selection, Time
|
||||
}
|
||||
|
||||
HistoryTextState HistoryGame::getState(QPoint point, HistoryStateRequest request) const {
|
||||
HistoryTextState result;
|
||||
auto result = HistoryTextState(_parent);
|
||||
|
||||
if (_width < st::msgPadding.left() + st::msgPadding.right() + 1) return result;
|
||||
if (_width < st::msgPadding.left() + st::msgPadding.right() + 1) {
|
||||
return result;
|
||||
}
|
||||
int32 width = _width, height = _height;
|
||||
|
||||
QMargins bubble(_attach ? _attach->bubbleMargins() : QMargins());
|
||||
@ -4416,7 +4469,11 @@ HistoryTextState HistoryGame::getState(QPoint point, HistoryStateRequest request
|
||||
if (point.y() >= tshift && point.y() < tshift + _titleLines * lineHeight) {
|
||||
Text::StateRequestElided titleRequest = request.forText();
|
||||
titleRequest.lines = _titleLines;
|
||||
result = _title.getStateElidedLeft(point - QPoint(padding.left(), tshift), width, _width, titleRequest);
|
||||
result = HistoryTextState(_parent, _title.getStateElidedLeft(
|
||||
point - QPoint(padding.left(), tshift),
|
||||
width,
|
||||
_width,
|
||||
titleRequest));
|
||||
} else if (point.y() >= tshift + _titleLines * lineHeight) {
|
||||
symbolAdd += _title.length();
|
||||
}
|
||||
@ -4426,7 +4483,11 @@ HistoryTextState HistoryGame::getState(QPoint point, HistoryStateRequest request
|
||||
if (point.y() >= tshift && point.y() < tshift + _descriptionLines * lineHeight) {
|
||||
Text::StateRequestElided descriptionRequest = request.forText();
|
||||
descriptionRequest.lines = _descriptionLines;
|
||||
result = _description.getStateElidedLeft(point - QPoint(padding.left(), tshift), width, _width, descriptionRequest);
|
||||
result = HistoryTextState(_parent, _description.getStateElidedLeft(
|
||||
point - QPoint(padding.left(), tshift),
|
||||
width,
|
||||
_width,
|
||||
descriptionRequest));
|
||||
} else if (point.y() >= tshift + _descriptionLines * lineHeight) {
|
||||
symbolAdd += _description.length();
|
||||
}
|
||||
@ -4576,7 +4637,7 @@ HistoryInvoice::HistoryInvoice(
|
||||
not_null<HistoryItem*> parent,
|
||||
const HistoryInvoice &other)
|
||||
: HistoryMedia(parent)
|
||||
, _attach(other._attach ? other._attach->clone(parent) : nullptr)
|
||||
, _attach(other._attach ? other._attach->clone(parent, parent) : nullptr)
|
||||
, _titleHeight(other._titleHeight)
|
||||
, _descriptionHeight(other._descriptionHeight)
|
||||
, _title(other._title)
|
||||
@ -4833,9 +4894,11 @@ void HistoryInvoice::draw(Painter &p, const QRect &r, TextSelection selection, T
|
||||
}
|
||||
|
||||
HistoryTextState HistoryInvoice::getState(QPoint point, HistoryStateRequest request) const {
|
||||
HistoryTextState result;
|
||||
auto result = HistoryTextState(_parent);
|
||||
|
||||
if (_width < st::msgPadding.left() + st::msgPadding.right() + 1) return result;
|
||||
if (_width < st::msgPadding.left() + st::msgPadding.right() + 1) {
|
||||
return result;
|
||||
}
|
||||
int32 width = _width, height = _height;
|
||||
|
||||
QMargins bubble(_attach ? _attach->bubbleMargins() : QMargins());
|
||||
@ -4853,7 +4916,11 @@ HistoryTextState HistoryInvoice::getState(QPoint point, HistoryStateRequest requ
|
||||
if (point.y() >= tshift && point.y() < tshift + _titleHeight) {
|
||||
Text::StateRequestElided titleRequest = request.forText();
|
||||
titleRequest.lines = _titleHeight / lineHeight;
|
||||
result = _title.getStateElidedLeft(point - QPoint(padding.left(), tshift), width, _width, titleRequest);
|
||||
result = HistoryTextState(_parent, _title.getStateElidedLeft(
|
||||
point - QPoint(padding.left(), tshift),
|
||||
width,
|
||||
_width,
|
||||
titleRequest));
|
||||
} else if (point.y() >= tshift + _titleHeight) {
|
||||
symbolAdd += _title.length();
|
||||
}
|
||||
@ -4861,7 +4928,11 @@ HistoryTextState HistoryInvoice::getState(QPoint point, HistoryStateRequest requ
|
||||
}
|
||||
if (_descriptionHeight) {
|
||||
if (point.y() >= tshift && point.y() < tshift + _descriptionHeight) {
|
||||
result = _description.getStateLeft(point - QPoint(padding.left(), tshift), width, _width, request.forText());
|
||||
result = HistoryTextState(_parent, _description.getStateLeft(
|
||||
point - QPoint(padding.left(), tshift),
|
||||
width,
|
||||
_width,
|
||||
request.forText()));
|
||||
} else if (point.y() >= tshift + _descriptionHeight) {
|
||||
symbolAdd += _description.length();
|
||||
}
|
||||
@ -5122,10 +5193,12 @@ void HistoryLocation::draw(Painter &p, const QRect &r, TextSelection selection,
|
||||
}
|
||||
|
||||
HistoryTextState HistoryLocation::getState(QPoint point, HistoryStateRequest request) const {
|
||||
HistoryTextState result;
|
||||
auto result = HistoryTextState(_parent);
|
||||
auto symbolAdd = 0;
|
||||
|
||||
if (_width < st::msgPadding.left() + st::msgPadding.right() + 1) return result;
|
||||
if (_width < st::msgPadding.left() + st::msgPadding.right() + 1) {
|
||||
return result;
|
||||
}
|
||||
int32 skipx = 0, skipy = 0, width = _width, height = _height;
|
||||
bool bubble = _parent->hasBubble();
|
||||
|
||||
@ -5145,7 +5218,11 @@ HistoryTextState HistoryLocation::getState(QPoint point, HistoryStateRequest req
|
||||
if (!_title.isEmpty()) {
|
||||
auto titleh = qMin(_title.countHeight(textw), 2 * st::webPageTitleFont->height);
|
||||
if (point.y() >= skipy && point.y() < skipy + titleh) {
|
||||
result = _title.getStateLeft(point - QPoint(skipx + st::msgPadding.left(), skipy), textw, _width, request.forText());
|
||||
result = HistoryTextState(_parent, _title.getStateLeft(
|
||||
point - QPoint(skipx + st::msgPadding.left(), skipy),
|
||||
textw,
|
||||
_width,
|
||||
request.forText()));
|
||||
return result;
|
||||
} else if (point.y() >= skipy + titleh) {
|
||||
symbolAdd += _title.length();
|
||||
@ -5155,7 +5232,11 @@ HistoryTextState HistoryLocation::getState(QPoint point, HistoryStateRequest req
|
||||
if (!_description.isEmpty()) {
|
||||
auto descriptionh = qMin(_description.countHeight(textw), 3 * st::webPageDescriptionFont->height);
|
||||
if (point.y() >= skipy && point.y() < skipy + descriptionh) {
|
||||
result = _description.getStateLeft(point - QPoint(skipx + st::msgPadding.left(), skipy), textw, _width, request.forText());
|
||||
result = HistoryTextState(_parent, _description.getStateLeft(
|
||||
point - QPoint(skipx + st::msgPadding.left(), skipy),
|
||||
textw,
|
||||
_width,
|
||||
request.forText()));
|
||||
} else if (point.y() >= skipy + descriptionh) {
|
||||
symbolAdd += _description.length();
|
||||
}
|
||||
|
@ -72,21 +72,28 @@ public:
|
||||
protected:
|
||||
ClickHandlerPtr _openl, _savel, _cancell;
|
||||
void setLinks(ClickHandlerPtr &&openl, ClickHandlerPtr &&savel, ClickHandlerPtr &&cancell);
|
||||
void setDocumentLinks(DocumentData *document, bool inlinegif = false) {
|
||||
void setDocumentLinks(
|
||||
not_null<DocumentData*> document,
|
||||
not_null<HistoryItem*> realParent,
|
||||
bool inlinegif = false) {
|
||||
ClickHandlerPtr open, save;
|
||||
const auto context = realParent->fullId();
|
||||
if (inlinegif) {
|
||||
open = MakeShared<GifOpenClickHandler>(document);
|
||||
open = MakeShared<GifOpenClickHandler>(document, context);
|
||||
} else {
|
||||
open = MakeShared<DocumentOpenClickHandler>(document);
|
||||
open = MakeShared<DocumentOpenClickHandler>(document, context);
|
||||
}
|
||||
if (inlinegif) {
|
||||
save = MakeShared<GifOpenClickHandler>(document);
|
||||
save = MakeShared<GifOpenClickHandler>(document, context);
|
||||
} else if (document->isVoiceMessage()) {
|
||||
save = MakeShared<DocumentOpenClickHandler>(document);
|
||||
save = MakeShared<DocumentOpenClickHandler>(document, context);
|
||||
} else {
|
||||
save = MakeShared<DocumentSaveClickHandler>(document);
|
||||
save = MakeShared<DocumentSaveClickHandler>(document, context);
|
||||
}
|
||||
setLinks(std::move(open), std::move(save), MakeShared<DocumentCancelClickHandler>(document));
|
||||
setLinks(
|
||||
std::move(open),
|
||||
std::move(save),
|
||||
MakeShared<DocumentCancelClickHandler>(document, context));
|
||||
}
|
||||
|
||||
// >= 0 will contain download / upload string, _statusSize = loaded bytes
|
||||
@ -153,15 +160,19 @@ public:
|
||||
not_null<PeerData*> chat,
|
||||
const MTPDphoto &photo,
|
||||
int width);
|
||||
HistoryPhoto(not_null<HistoryItem*> parent, const HistoryPhoto &other);
|
||||
HistoryPhoto(
|
||||
not_null<HistoryItem*> parent,
|
||||
not_null<HistoryItem*> realParent,
|
||||
const HistoryPhoto &other);
|
||||
|
||||
void init();
|
||||
HistoryMediaType type() const override {
|
||||
return MediaTypePhoto;
|
||||
}
|
||||
std::unique_ptr<HistoryMedia> clone(
|
||||
not_null<HistoryItem*> newParent) const override {
|
||||
return std::make_unique<HistoryPhoto>(newParent, *this);
|
||||
not_null<HistoryItem*> newParent,
|
||||
not_null<HistoryItem*> realParent) const override {
|
||||
return std::make_unique<HistoryPhoto>(newParent, realParent, *this);
|
||||
}
|
||||
|
||||
void initDimensions() override;
|
||||
@ -269,14 +280,18 @@ public:
|
||||
not_null<HistoryItem*> parent,
|
||||
not_null<DocumentData*> document,
|
||||
const QString &caption);
|
||||
HistoryVideo(not_null<HistoryItem*> parent, const HistoryVideo &other);
|
||||
HistoryVideo(
|
||||
not_null<HistoryItem*> parent,
|
||||
not_null<HistoryItem*> realParent,
|
||||
const HistoryVideo &other);
|
||||
|
||||
HistoryMediaType type() const override {
|
||||
return MediaTypeVideo;
|
||||
}
|
||||
std::unique_ptr<HistoryMedia> clone(
|
||||
not_null<HistoryItem*> newParent) const override {
|
||||
return std::make_unique<HistoryVideo>(newParent, *this);
|
||||
not_null<HistoryItem*> newParent,
|
||||
not_null<HistoryItem*> realParent) const override {
|
||||
return std::make_unique<HistoryVideo>(newParent, realParent, *this);
|
||||
}
|
||||
|
||||
void initDimensions() override;
|
||||
@ -460,7 +475,10 @@ public:
|
||||
: MediaTypeFile);
|
||||
}
|
||||
std::unique_ptr<HistoryMedia> clone(
|
||||
not_null<HistoryItem*> newParent) const override {
|
||||
not_null<HistoryItem*> newParent,
|
||||
not_null<HistoryItem*> realParent) const override {
|
||||
Expects(newParent == realParent);
|
||||
|
||||
return std::make_unique<HistoryDocument>(newParent, *this);
|
||||
}
|
||||
|
||||
@ -579,7 +597,10 @@ public:
|
||||
return MediaTypeGif;
|
||||
}
|
||||
std::unique_ptr<HistoryMedia> clone(
|
||||
not_null<HistoryItem*> newParent) const override {
|
||||
not_null<HistoryItem*> newParent,
|
||||
not_null<HistoryItem*> realParent) const override {
|
||||
Expects(newParent == realParent);
|
||||
|
||||
return std::make_unique<HistoryGif>(newParent, *this);
|
||||
}
|
||||
|
||||
@ -696,7 +717,10 @@ public:
|
||||
return MediaTypeSticker;
|
||||
}
|
||||
std::unique_ptr<HistoryMedia> clone(
|
||||
not_null<HistoryItem*> newParent) const override {
|
||||
not_null<HistoryItem*> newParent,
|
||||
not_null<HistoryItem*> realParent) const override {
|
||||
Expects(newParent == realParent);
|
||||
|
||||
return std::make_unique<HistorySticker>(newParent, _data);
|
||||
}
|
||||
|
||||
@ -772,8 +796,16 @@ public:
|
||||
return MediaTypeContact;
|
||||
}
|
||||
std::unique_ptr<HistoryMedia> clone(
|
||||
not_null<HistoryItem*> newParent) const override {
|
||||
return std::make_unique<HistoryContact>(newParent, _userId, _fname, _lname, _phone);
|
||||
not_null<HistoryItem*> newParent,
|
||||
not_null<HistoryItem*> realParent) const override {
|
||||
Expects(newParent == realParent);
|
||||
|
||||
return std::make_unique<HistoryContact>(
|
||||
newParent,
|
||||
_userId,
|
||||
_fname,
|
||||
_lname,
|
||||
_phone);
|
||||
}
|
||||
|
||||
void initDimensions() override;
|
||||
@ -840,7 +872,8 @@ public:
|
||||
return MediaTypeCall;
|
||||
}
|
||||
std::unique_ptr<HistoryMedia> clone(
|
||||
not_null<HistoryItem*> newParent) const override {
|
||||
not_null<HistoryItem*> newParent,
|
||||
not_null<HistoryItem*> realParent) const override {
|
||||
Unexpected("Clone HistoryCall.");
|
||||
}
|
||||
|
||||
@ -902,7 +935,10 @@ public:
|
||||
return MediaTypeWebPage;
|
||||
}
|
||||
std::unique_ptr<HistoryMedia> clone(
|
||||
not_null<HistoryItem*> newParent) const override {
|
||||
not_null<HistoryItem*> newParent,
|
||||
not_null<HistoryItem*> realParent) const override {
|
||||
Expects(newParent == realParent);
|
||||
|
||||
return std::make_unique<HistoryWebPage>(newParent, *this);
|
||||
}
|
||||
|
||||
@ -1012,7 +1048,10 @@ public:
|
||||
return MediaTypeGame;
|
||||
}
|
||||
std::unique_ptr<HistoryMedia> clone(
|
||||
not_null<HistoryItem*> newParent) const override {
|
||||
not_null<HistoryItem*> newParent,
|
||||
not_null<HistoryItem*> realParent) const override {
|
||||
Expects(newParent == realParent);
|
||||
|
||||
return std::make_unique<HistoryGame>(newParent, *this);
|
||||
}
|
||||
|
||||
@ -1127,7 +1166,10 @@ public:
|
||||
return MediaTypeInvoice;
|
||||
}
|
||||
std::unique_ptr<HistoryMedia> clone(
|
||||
not_null<HistoryItem*> newParent) const override {
|
||||
not_null<HistoryItem*> newParent,
|
||||
not_null<HistoryItem*> realParent) const override {
|
||||
Expects(newParent == realParent);
|
||||
|
||||
return std::make_unique<HistoryInvoice>(newParent, *this);
|
||||
}
|
||||
|
||||
@ -1231,7 +1273,10 @@ public:
|
||||
return MediaTypeLocation;
|
||||
}
|
||||
std::unique_ptr<HistoryMedia> clone(
|
||||
not_null<HistoryItem*> newParent) const override {
|
||||
not_null<HistoryItem*> newParent,
|
||||
not_null<HistoryItem*> realParent) const override {
|
||||
Expects(newParent == realParent);
|
||||
|
||||
return std::make_unique<HistoryLocation>(newParent, *this);
|
||||
}
|
||||
|
||||
|
@ -757,7 +757,7 @@ HistoryMessage::HistoryMessage(
|
||||
return (mediaType != MediaTypeCount);
|
||||
};
|
||||
if (cloneMedia()) {
|
||||
_media = mediaOriginal->clone(this);
|
||||
_media = mediaOriginal->clone(this, this);
|
||||
}
|
||||
setText(fwd->originalText());
|
||||
}
|
||||
@ -1512,12 +1512,22 @@ Storage::SharedMediaTypesMask HistoryMessage::sharedMediaTypes() const {
|
||||
|
||||
TextWithEntities HistoryMessage::selectedText(TextSelection selection) const {
|
||||
TextWithEntities logEntryOriginalResult;
|
||||
auto textResult = _text.originalTextWithEntities((selection == FullSelection) ? AllTextSelection : selection, ExpandLinksAll);
|
||||
const auto textSelection = (selection == FullSelection)
|
||||
? AllTextSelection
|
||||
: IsSubGroupSelection(selection)
|
||||
? TextSelection(0, 0)
|
||||
: selection;
|
||||
auto textResult = _text.originalTextWithEntities(
|
||||
textSelection,
|
||||
ExpandLinksAll);
|
||||
auto skipped = skipTextSelection(selection);
|
||||
auto mediaDisplayed = (_media && _media->isDisplayed());
|
||||
auto mediaResult = mediaDisplayed ? _media->selectedText(skipped) : TextWithEntities();
|
||||
if (auto entry = Get<HistoryMessageLogEntryOriginal>()) {
|
||||
logEntryOriginalResult = entry->_page->selectedText(mediaDisplayed ? _media->skipSelection(skipped) : skipped);
|
||||
const auto originalSelection = mediaDisplayed
|
||||
? _media->skipSelection(skipped)
|
||||
: skipped;
|
||||
logEntryOriginalResult = entry->_page->selectedText(originalSelection);
|
||||
}
|
||||
auto result = textResult;
|
||||
if (result.text.isEmpty()) {
|
||||
@ -2232,7 +2242,7 @@ bool HistoryMessage::pointInTime(int right, int bottom, QPoint point, InfoDispla
|
||||
}
|
||||
|
||||
HistoryTextState HistoryMessage::getState(QPoint point, HistoryStateRequest request) const {
|
||||
HistoryTextState result;
|
||||
auto result = HistoryTextState(this);
|
||||
|
||||
auto g = countGeometry();
|
||||
if (g.width() < 1) {
|
||||
@ -2272,7 +2282,9 @@ HistoryTextState HistoryMessage::getState(QPoint point, HistoryStateRequest requ
|
||||
auto entryLeft = g.left();
|
||||
auto entryTop = trect.y() + trect.height();
|
||||
if (point.y() >= entryTop && point.y() < entryTop + entryHeight) {
|
||||
result = entry->_page->getState(point - QPoint(entryLeft, entryTop), request);
|
||||
result = entry->_page->getState(
|
||||
point - QPoint(entryLeft, entryTop),
|
||||
request);
|
||||
result.symbol += _text.length() + (mediaDisplayed ? _media->fullSelectionLength() : 0);
|
||||
}
|
||||
}
|
||||
@ -2408,7 +2420,10 @@ void HistoryMessage::updatePressed(QPoint point) {
|
||||
}
|
||||
}
|
||||
|
||||
bool HistoryMessage::getStateFromName(QPoint point, QRect &trect, HistoryTextState *outResult) const {
|
||||
bool HistoryMessage::getStateFromName(
|
||||
QPoint point,
|
||||
QRect &trect,
|
||||
not_null<HistoryTextState*> outResult) const {
|
||||
if (displayFromName()) {
|
||||
if (point.y() >= trect.top() && point.y() < trect.top() + st::msgNameFont->height) {
|
||||
auto user = displayFrom();
|
||||
@ -2428,7 +2443,11 @@ bool HistoryMessage::getStateFromName(QPoint point, QRect &trect, HistoryTextSta
|
||||
return false;
|
||||
}
|
||||
|
||||
bool HistoryMessage::getStateForwardedInfo(QPoint point, QRect &trect, HistoryTextState *outResult, const HistoryStateRequest &request) const {
|
||||
bool HistoryMessage::getStateForwardedInfo(
|
||||
QPoint point,
|
||||
QRect &trect,
|
||||
not_null<HistoryTextState*> outResult,
|
||||
const HistoryStateRequest &request) const {
|
||||
if (displayForwardedFrom()) {
|
||||
auto forwarded = Get<HistoryMessageForwarded>();
|
||||
auto fwdheight = ((forwarded->_text.maxWidth() > trect.width()) ? 2 : 1) * st::semiboldFont->height;
|
||||
@ -2438,7 +2457,10 @@ bool HistoryMessage::getStateForwardedInfo(QPoint point, QRect &trect, HistoryTe
|
||||
if (breakEverywhere) {
|
||||
textRequest.flags |= Text::StateRequest::Flag::BreakEverywhere;
|
||||
}
|
||||
*outResult = forwarded->_text.getState(point - trect.topLeft(), trect.width(), textRequest);
|
||||
*outResult = HistoryTextState(this, forwarded->_text.getState(
|
||||
point - trect.topLeft(),
|
||||
trect.width(),
|
||||
textRequest));
|
||||
outResult->symbol = 0;
|
||||
outResult->afterSymbol = false;
|
||||
if (breakEverywhere) {
|
||||
@ -2453,7 +2475,10 @@ bool HistoryMessage::getStateForwardedInfo(QPoint point, QRect &trect, HistoryTe
|
||||
return false;
|
||||
}
|
||||
|
||||
bool HistoryMessage::getStateReplyInfo(QPoint point, QRect &trect, HistoryTextState *outResult) const {
|
||||
bool HistoryMessage::getStateReplyInfo(
|
||||
QPoint point,
|
||||
QRect &trect,
|
||||
not_null<HistoryTextState*> outResult) const {
|
||||
if (auto reply = Get<HistoryMessageReply>()) {
|
||||
int32 h = st::msgReplyPadding.top() + st::msgReplyBarSize.height() + st::msgReplyPadding.bottom();
|
||||
if (point.y() >= trect.top() && point.y() < trect.top() + h) {
|
||||
@ -2467,7 +2492,10 @@ bool HistoryMessage::getStateReplyInfo(QPoint point, QRect &trect, HistoryTextSt
|
||||
return false;
|
||||
}
|
||||
|
||||
bool HistoryMessage::getStateViaBotIdInfo(QPoint point, QRect &trect, HistoryTextState *outResult) const {
|
||||
bool HistoryMessage::getStateViaBotIdInfo(
|
||||
QPoint point,
|
||||
QRect &trect,
|
||||
not_null<HistoryTextState*> outResult) const {
|
||||
if (!displayFromName() && !Has<HistoryMessageForwarded>()) {
|
||||
if (auto via = Get<HistoryMessageVia>()) {
|
||||
if (QRect(trect.x(), trect.y(), via->_width, st::msgNameFont->height).contains(point)) {
|
||||
@ -2480,9 +2508,16 @@ bool HistoryMessage::getStateViaBotIdInfo(QPoint point, QRect &trect, HistoryTex
|
||||
return false;
|
||||
}
|
||||
|
||||
bool HistoryMessage::getStateText(QPoint point, QRect &trect, HistoryTextState *outResult, const HistoryStateRequest &request) const {
|
||||
bool HistoryMessage::getStateText(
|
||||
QPoint point,
|
||||
QRect &trect,
|
||||
not_null<HistoryTextState*> outResult,
|
||||
const HistoryStateRequest &request) const {
|
||||
if (trect.contains(point)) {
|
||||
*outResult = _text.getState(point - trect.topLeft(), trect.width(), request.forText());
|
||||
*outResult = HistoryTextState(this, _text.getState(
|
||||
point - trect.topLeft(),
|
||||
trect.width(),
|
||||
request.forText()));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@ -2524,13 +2559,18 @@ TextSelection HistoryMessage::adjustSelection(TextSelection selection, TextSelec
|
||||
}
|
||||
|
||||
void HistoryMessage::clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) {
|
||||
if (_media) _media->clickHandlerActiveChanged(p, active);
|
||||
HistoryItem::clickHandlerActiveChanged(p, active);
|
||||
if (_media) {
|
||||
_media->clickHandlerActiveChanged(p, active);
|
||||
}
|
||||
}
|
||||
|
||||
void HistoryMessage::clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed) {
|
||||
if (_media) _media->clickHandlerPressedChanged(p, pressed);
|
||||
HistoryItem::clickHandlerPressedChanged(p, pressed);
|
||||
if (_media) {
|
||||
// HistoryGroupedMedia overrides HistoryItem App::pressedLinkItem().
|
||||
_media->clickHandlerPressedChanged(p, pressed);
|
||||
}
|
||||
}
|
||||
|
||||
QString HistoryMessage::notificationHeader() const {
|
||||
|
@ -329,11 +329,28 @@ private:
|
||||
void paintViaBotIdInfo(Painter &p, QRect &trect, bool selected) const;
|
||||
void paintText(Painter &p, QRect &trect, TextSelection selection) const;
|
||||
|
||||
bool getStateFromName(QPoint point, QRect &trect, HistoryTextState *outResult) const;
|
||||
bool getStateForwardedInfo(QPoint point, QRect &trect, HistoryTextState *outResult, const HistoryStateRequest &request) const;
|
||||
bool getStateReplyInfo(QPoint point, QRect &trect, HistoryTextState *outResult) const;
|
||||
bool getStateViaBotIdInfo(QPoint point, QRect &trect, HistoryTextState *outResult) const;
|
||||
bool getStateText(QPoint point, QRect &trect, HistoryTextState *outResult, const HistoryStateRequest &request) const;
|
||||
bool getStateFromName(
|
||||
QPoint point,
|
||||
QRect &trect,
|
||||
not_null<HistoryTextState*> outResult) const;
|
||||
bool getStateForwardedInfo(
|
||||
QPoint point,
|
||||
QRect &trect,
|
||||
not_null<HistoryTextState*> outResult,
|
||||
const HistoryStateRequest &request) const;
|
||||
bool getStateReplyInfo(
|
||||
QPoint point,
|
||||
QRect &trect,
|
||||
not_null<HistoryTextState*> outResult) const;
|
||||
bool getStateViaBotIdInfo(
|
||||
QPoint point,
|
||||
QRect &trect,
|
||||
not_null<HistoryTextState*> outResult) const;
|
||||
bool getStateText(
|
||||
QPoint point,
|
||||
QRect &trect,
|
||||
not_null<HistoryTextState*> outResult,
|
||||
const HistoryStateRequest &request) const;
|
||||
|
||||
void setMedia(const MTPMessageMedia *media);
|
||||
void setReplyMarkup(const MTPReplyMarkup *markup);
|
||||
|
@ -594,7 +594,7 @@ bool HistoryService::hasPoint(QPoint point) const {
|
||||
}
|
||||
|
||||
HistoryTextState HistoryService::getState(QPoint point, HistoryStateRequest request) const {
|
||||
HistoryTextState result;
|
||||
auto result = HistoryTextState(this);
|
||||
|
||||
auto g = countGeometry();
|
||||
if (g.width() < 1) {
|
||||
@ -618,7 +618,10 @@ HistoryTextState HistoryService::getState(QPoint point, HistoryStateRequest requ
|
||||
if (trect.contains(point)) {
|
||||
auto textRequest = request.forText();
|
||||
textRequest.align = style::al_center;
|
||||
result = _text.getState(point - trect.topLeft(), trect.width(), textRequest);
|
||||
result = HistoryTextState(this, _text.getState(
|
||||
point - trect.topLeft(),
|
||||
trect.width(),
|
||||
textRequest));
|
||||
if (auto gamescore = Get<HistoryServiceGameScore>()) {
|
||||
if (!result.link && result.cursor == HistoryInTextCursorState && g.contains(point)) {
|
||||
result.link = gamescore->lnk;
|
||||
|
@ -218,9 +218,9 @@ HistoryTextState Gif::getState(
|
||||
HistoryStateRequest request) const {
|
||||
if (QRect(0, 0, _width, st::inlineMediaHeight).contains(point)) {
|
||||
if (_delete && rtlpoint(point, _width).x() >= _width - st::stickerPanDeleteIconBg.width() && point.y() < st::stickerPanDeleteIconBg.height()) {
|
||||
return _delete;
|
||||
return { nullptr, _delete };
|
||||
} else {
|
||||
return _send;
|
||||
return { nullptr, _send };
|
||||
}
|
||||
}
|
||||
return {};
|
||||
@ -411,7 +411,7 @@ HistoryTextState Sticker::getState(
|
||||
QPoint point,
|
||||
HistoryStateRequest request) const {
|
||||
if (QRect(0, 0, _width, st::inlineMediaHeight).contains(point)) {
|
||||
return _send;
|
||||
return { nullptr, _send };
|
||||
}
|
||||
return {};
|
||||
}
|
||||
@ -501,7 +501,7 @@ HistoryTextState Photo::getState(
|
||||
QPoint point,
|
||||
HistoryStateRequest request) const {
|
||||
if (QRect(0, 0, _width, st::inlineMediaHeight).contains(point)) {
|
||||
return _send;
|
||||
return { nullptr, _send };
|
||||
}
|
||||
return {};
|
||||
}
|
||||
@ -646,10 +646,10 @@ HistoryTextState Video::getState(
|
||||
QPoint point,
|
||||
HistoryStateRequest request) const {
|
||||
if (QRect(0, st::inlineRowMargin, st::inlineThumbSize, st::inlineThumbSize).contains(point)) {
|
||||
return _link;
|
||||
return { nullptr, _link };
|
||||
}
|
||||
if (QRect(st::inlineThumbSize + st::inlineThumbSkip, 0, _width - st::inlineThumbSize - st::inlineThumbSkip, _height).contains(point)) {
|
||||
return _send;
|
||||
return { nullptr, _send };
|
||||
}
|
||||
return {};
|
||||
}
|
||||
@ -787,11 +787,11 @@ HistoryTextState File::getState(
|
||||
QPoint point,
|
||||
HistoryStateRequest request) const {
|
||||
if (QRect(0, st::inlineRowMargin, st::msgFileSize, st::msgFileSize).contains(point)) {
|
||||
return getShownDocument()->loading() ? _cancel : _open;
|
||||
return { nullptr, getShownDocument()->loading() ? _cancel : _open };
|
||||
} else {
|
||||
auto left = st::msgFileSize + st::inlineThumbSkip;
|
||||
if (QRect(left, 0, _width - left, _height).contains(point)) {
|
||||
return _send;
|
||||
return { nullptr, _send };
|
||||
}
|
||||
}
|
||||
return {};
|
||||
@ -955,7 +955,7 @@ HistoryTextState Contact::getState(
|
||||
if (!QRect(0, st::inlineRowMargin, st::msgFileSize, st::inlineThumbSize).contains(point)) {
|
||||
auto left = (st::msgFileSize + st::inlineThumbSkip);
|
||||
if (QRect(left, 0, _width - left, _height).contains(point)) {
|
||||
return _send;
|
||||
return { nullptr, _send };
|
||||
}
|
||||
}
|
||||
return {};
|
||||
@ -1090,7 +1090,7 @@ HistoryTextState Article::getState(
|
||||
QPoint point,
|
||||
HistoryStateRequest request) const {
|
||||
if (_withThumb && QRect(0, st::inlineRowMargin, st::inlineThumbSize, st::inlineThumbSize).contains(point)) {
|
||||
return _link;
|
||||
return { nullptr, _link };
|
||||
}
|
||||
auto left = _withThumb ? (st::inlineThumbSize + st::inlineThumbSkip) : 0;
|
||||
if (QRect(left, 0, _width - left, _height).contains(point)) {
|
||||
@ -1100,10 +1100,10 @@ HistoryTextState Article::getState(
|
||||
auto descriptionLines = 2;
|
||||
auto descriptionHeight = qMin(_description.countHeight(_width - left), st::normalFont->height * descriptionLines);
|
||||
if (rtlrect(left, st::inlineRowMargin + titleHeight + descriptionHeight, _urlWidth, st::normalFont->height, _width).contains(point)) {
|
||||
return _url;
|
||||
return { nullptr, _url };
|
||||
}
|
||||
}
|
||||
return _send;
|
||||
return { nullptr, _send };
|
||||
}
|
||||
return {};
|
||||
}
|
||||
@ -1275,23 +1275,23 @@ HistoryTextState Game::getState(
|
||||
HistoryStateRequest request) const {
|
||||
int left = st::inlineThumbSize + st::inlineThumbSkip;
|
||||
if (QRect(0, st::inlineRowMargin, st::inlineThumbSize, st::inlineThumbSize).contains(point)) {
|
||||
return _send;
|
||||
return { nullptr, _send };
|
||||
}
|
||||
if (QRect(left, 0, _width - left, _height).contains(point)) {
|
||||
return _send;
|
||||
return { nullptr, _send };
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
void Game::prepareThumb(int width, int height) const {
|
||||
auto thumb = ([this]() {
|
||||
auto thumb = [this] {
|
||||
if (auto photo = getResultPhoto()) {
|
||||
return photo->medium;
|
||||
} else if (auto document = getResultDocument()) {
|
||||
return document->thumb;
|
||||
}
|
||||
return ImagePtr();
|
||||
})();
|
||||
}();
|
||||
if (thumb->isNull()) {
|
||||
return;
|
||||
}
|
||||
|
@ -24,6 +24,40 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
|
||||
constexpr auto FullSelection = TextSelection { 0xFFFF, 0xFFFF };
|
||||
|
||||
inline bool IsSubGroupSelection(TextSelection selection) {
|
||||
return (selection.from == 0xFFFF) && (selection.to != 0xFFFF);
|
||||
}
|
||||
|
||||
inline bool IsGroupItemSelection(
|
||||
TextSelection selection,
|
||||
int index) {
|
||||
Expects(index >= 0 && index < 0x0F);
|
||||
|
||||
return IsSubGroupSelection(selection) && (selection.to & (1 << index));
|
||||
}
|
||||
|
||||
inline [[nodiscard]] TextSelection AddGroupItemSelection(
|
||||
TextSelection selection,
|
||||
int index) {
|
||||
Expects(index >= 0 && index < 0x0F);
|
||||
|
||||
const auto bit = uint16(1U << index);
|
||||
return TextSelection(
|
||||
0xFFFF,
|
||||
IsSubGroupSelection(selection) ? (selection.to | bit) : bit);
|
||||
}
|
||||
|
||||
inline[[nodiscard]] TextSelection RemoveGroupItemSelection(
|
||||
TextSelection selection,
|
||||
int index) {
|
||||
Expects(index >= 0 && index < 0x0F);
|
||||
|
||||
const auto bit = uint16(1U << index);
|
||||
return IsSubGroupSelection(selection)
|
||||
? TextSelection(0xFFFF, selection.to & ~bit)
|
||||
: selection;
|
||||
}
|
||||
|
||||
extern TextParseOptions _textNameOptions, _textDlgOptions;
|
||||
extern TextParseOptions _historyTextOptions, _historyBotOptions, _historyTextNoMonoOptions, _historyBotNoMonoOptions;
|
||||
|
||||
|
@ -201,9 +201,11 @@ bool Messenger::hideMediaView() {
|
||||
return false;
|
||||
}
|
||||
|
||||
void Messenger::showPhoto(not_null<const PhotoOpenClickHandler*> link, HistoryItem *item) {
|
||||
return (!item && link->peer())
|
||||
? showPhoto(link->photo(), link->peer())
|
||||
void Messenger::showPhoto(not_null<const PhotoOpenClickHandler*> link) {
|
||||
const auto item = App::histItemById(link->context());
|
||||
const auto peer = link->peer();
|
||||
return (!item && peer)
|
||||
? showPhoto(link->photo(), peer)
|
||||
: showPhoto(link->photo(), item);
|
||||
}
|
||||
|
||||
@ -214,7 +216,9 @@ void Messenger::showPhoto(not_null<PhotoData*> photo, HistoryItem *item) {
|
||||
_mediaView->setFocus();
|
||||
}
|
||||
|
||||
void Messenger::showPhoto(not_null<PhotoData*> photo, PeerData *peer) {
|
||||
void Messenger::showPhoto(
|
||||
not_null<PhotoData*> photo,
|
||||
not_null<PeerData*> peer) {
|
||||
if (_mediaView->isHidden()) Ui::hideLayer(anim::type::instant);
|
||||
_mediaView->showPhoto(photo, peer);
|
||||
_mediaView->activateWindow();
|
||||
|
@ -86,9 +86,9 @@ public:
|
||||
// MediaView interface.
|
||||
void checkMediaViewActivation();
|
||||
bool hideMediaView();
|
||||
void showPhoto(not_null<const PhotoOpenClickHandler*> link, HistoryItem *item = nullptr);
|
||||
void showPhoto(not_null<const PhotoOpenClickHandler*> link);
|
||||
void showPhoto(not_null<PhotoData*> photo, HistoryItem *item);
|
||||
void showPhoto(not_null<PhotoData*> photo, PeerData *item);
|
||||
void showPhoto(not_null<PhotoData*> photo, not_null<PeerData*> item);
|
||||
void showDocument(not_null<DocumentData*> document, HistoryItem *item);
|
||||
PeerData *ui_getPeerForMouseAction();
|
||||
|
||||
|
@ -283,7 +283,7 @@ Photo::Photo(
|
||||
not_null<PhotoData*> photo)
|
||||
: ItemBase(parent)
|
||||
, _data(photo)
|
||||
, _link(MakeShared<PhotoOpenClickHandler>(photo)) {
|
||||
, _link(MakeShared<PhotoOpenClickHandler>(photo, parent->fullId())) {
|
||||
}
|
||||
|
||||
void Photo::initDimensions() {
|
||||
@ -352,7 +352,7 @@ HistoryTextState Photo::getState(
|
||||
QPoint point,
|
||||
HistoryStateRequest request) const {
|
||||
if (hasPoint(point)) {
|
||||
return _link;
|
||||
return { parent(), _link };
|
||||
}
|
||||
return {};
|
||||
}
|
||||
@ -508,7 +508,12 @@ HistoryTextState Video::getState(
|
||||
bool loaded = _data->loaded();
|
||||
|
||||
if (hasPoint(point)) {
|
||||
return loaded ? _openl : (_data->loading() ? _cancell : _savel);
|
||||
const auto link = loaded
|
||||
? _openl
|
||||
: _data->loading()
|
||||
? _cancell
|
||||
: _savel;
|
||||
return { parent(), link };
|
||||
}
|
||||
return {};
|
||||
}
|
||||
@ -687,13 +692,14 @@ HistoryTextState Voice::getState(
|
||||
_st.songThumbSize,
|
||||
_width);
|
||||
if (inner.contains(point)) {
|
||||
return loaded
|
||||
const auto link = loaded
|
||||
? _openl
|
||||
: ((_data->loading() || _data->status == FileUploading)
|
||||
? _cancell
|
||||
: _openl);
|
||||
: (_data->loading() || _data->status == FileUploading)
|
||||
? _cancell
|
||||
: _openl;
|
||||
return { parent(), link };
|
||||
}
|
||||
auto result = HistoryTextState();
|
||||
auto result = HistoryTextState(parent());
|
||||
const auto statusmaxwidth = _width - nameleft - nameright;
|
||||
const auto statusrect = rtlrect(
|
||||
nameleft,
|
||||
@ -718,7 +724,7 @@ HistoryTextState Voice::getState(
|
||||
st::normalFont->height,
|
||||
_width);
|
||||
if (namerect.contains(point) && !result.link && !_data->loading()) {
|
||||
return _namel;
|
||||
return { parent(), _namel };
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@ -1014,11 +1020,12 @@ HistoryTextState Document::getState(
|
||||
_st.songThumbSize,
|
||||
_width);
|
||||
if (inner.contains(point)) {
|
||||
return loaded
|
||||
const auto link = loaded
|
||||
? _openl
|
||||
: ((_data->loading() || _data->status == FileUploading)
|
||||
? _cancell
|
||||
: _openl);
|
||||
: (_data->loading() || _data->status == FileUploading)
|
||||
? _cancell
|
||||
: _openl;
|
||||
return { parent(), link };
|
||||
}
|
||||
const auto namerect = rtlrect(
|
||||
nameleft,
|
||||
@ -1027,7 +1034,7 @@ HistoryTextState Document::getState(
|
||||
st::semiboldFont->height,
|
||||
_width);
|
||||
if (namerect.contains(point) && !_data->loading()) {
|
||||
return _namel;
|
||||
return { parent(), _namel };
|
||||
}
|
||||
} else {
|
||||
const auto nameleft = _st.fileThumbSize + _st.filePadding.right();
|
||||
@ -1047,11 +1054,12 @@ HistoryTextState Document::getState(
|
||||
_width);
|
||||
|
||||
if (rthumb.contains(point)) {
|
||||
return loaded
|
||||
const auto link = loaded
|
||||
? _openl
|
||||
: ((_data->loading() || _data->status == FileUploading)
|
||||
? _cancell
|
||||
: _savel);
|
||||
: (_data->loading() || _data->status == FileUploading)
|
||||
? _cancell
|
||||
: _savel;
|
||||
return { parent(), link };
|
||||
}
|
||||
|
||||
if (_data->status != FileUploadFailed) {
|
||||
@ -1062,7 +1070,7 @@ HistoryTextState Document::getState(
|
||||
st::normalFont->height,
|
||||
_width);
|
||||
if (daterect.contains(point)) {
|
||||
return _msgl;
|
||||
return { parent(), _msgl };
|
||||
}
|
||||
}
|
||||
if (!_data->loading() && _data->isValid()) {
|
||||
@ -1073,7 +1081,7 @@ HistoryTextState Document::getState(
|
||||
_height - st::linksBorder,
|
||||
_width);
|
||||
if (loaded && leftofnamerect.contains(point)) {
|
||||
return _namel;
|
||||
return { parent(), _namel };
|
||||
}
|
||||
const auto namerect = rtlrect(
|
||||
nameleft,
|
||||
@ -1082,7 +1090,7 @@ HistoryTextState Document::getState(
|
||||
st::semiboldFont->height,
|
||||
_width);
|
||||
if (namerect.contains(point)) {
|
||||
return _namel;
|
||||
return { parent(), _namel };
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1212,7 +1220,9 @@ Link::Link(
|
||||
if (_page->type == WebPageProfile || _page->type == WebPageVideo) {
|
||||
_photol = MakeShared<UrlClickHandler>(_page->url);
|
||||
} else if (_page->type == WebPagePhoto || _page->siteName == qstr("Twitter") || _page->siteName == qstr("Facebook")) {
|
||||
_photol = MakeShared<PhotoOpenClickHandler>(_page->photo);
|
||||
_photol = MakeShared<PhotoOpenClickHandler>(
|
||||
_page->photo,
|
||||
parent->fullId());
|
||||
} else {
|
||||
_photol = MakeShared<UrlClickHandler>(_page->url);
|
||||
}
|
||||
@ -1415,7 +1425,7 @@ HistoryTextState Link::getState(
|
||||
HistoryStateRequest request) const {
|
||||
int32 left = st::linksPhotoSize + st::linksPhotoPadding, top = st::linksMargin.top() + st::linksBorder, w = _width - left;
|
||||
if (rtlrect(0, top, st::linksPhotoSize, st::linksPhotoSize, _width).contains(point)) {
|
||||
return _photol;
|
||||
return { parent(), _photol };
|
||||
}
|
||||
|
||||
if (!_title.isEmpty() && _text.isEmpty() && _links.size() == 1) {
|
||||
@ -1423,7 +1433,7 @@ HistoryTextState Link::getState(
|
||||
}
|
||||
if (!_title.isEmpty()) {
|
||||
if (rtlrect(left, top, qMin(w, _titlew), st::semiboldFont->height, _width).contains(point)) {
|
||||
return _photol;
|
||||
return { parent(), _photol };
|
||||
}
|
||||
top += st::webPageTitleFont->height;
|
||||
}
|
||||
@ -1432,7 +1442,7 @@ HistoryTextState Link::getState(
|
||||
}
|
||||
for (int32 i = 0, l = _links.size(); i < l; ++i) {
|
||||
if (rtlrect(left, top, qMin(w, _links.at(i).width), st::normalFont->height, _width).contains(point)) {
|
||||
return ClickHandlerPtr(_links[i].lnk);
|
||||
return { parent(), ClickHandlerPtr(_links[i].lnk) };
|
||||
}
|
||||
top += st::normalFont->height;
|
||||
}
|
||||
|
@ -94,6 +94,8 @@ CoverWidget::CoverWidget(QWidget *parent, UserData *self)
|
||||
}
|
||||
|
||||
PhotoData *CoverWidget::validatePhoto() const {
|
||||
Expects(_self != nullptr);
|
||||
|
||||
const auto photo = _self->userpicPhotoId()
|
||||
? App::photo(_self->userpicPhotoId())
|
||||
: nullptr;
|
||||
@ -106,7 +108,7 @@ PhotoData *CoverWidget::validatePhoto() const {
|
||||
}
|
||||
|
||||
void CoverWidget::showPhoto() {
|
||||
if (auto photo = validatePhoto()) {
|
||||
if (const auto photo = validatePhoto()) {
|
||||
Messenger::Instance().showPhoto(photo, _self);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user