tdesktop/Telegram/SourceFiles/history/view/media/history_view_location.cpp

312 lines
10 KiB
C++

/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "history/view/media/history_view_location.h"
#include "layout.h"
#include "history/history_item_components.h"
#include "history/history_item.h"
#include "history/history_location_manager.h"
#include "history/view/history_view_element.h"
#include "history/view/history_view_cursor_state.h"
#include "ui/image/image.h"
#include "ui/text_options.h"
#include "data/data_file_origin.h"
#include "data/data_location.h"
#include "app.h"
#include "styles/style_history.h"
namespace HistoryView {
Location::Location(
not_null<Element*> parent,
not_null<Data::LocationThumbnail*> location,
const QString &title,
const QString &description)
: Media(parent)
, _data(location)
, _title(st::msgMinWidth)
, _description(st::msgMinWidth)
, _link(std::make_shared<LocationClickHandler>(_data->point)) {
if (!title.isEmpty()) {
_title.setText(
st::webPageTitleStyle,
TextUtilities::Clean(title),
Ui::WebpageTextTitleOptions());
}
if (!description.isEmpty()) {
_description.setMarkedText(
st::webPageDescriptionStyle,
TextUtilities::ParseEntities(
TextUtilities::Clean(description),
TextParseLinks | TextParseMultiline | TextParseRichText),
Ui::WebpageTextDescriptionOptions());
}
}
QSize Location::countOptimalSize() {
auto tw = fullWidth();
auto th = fullHeight();
if (tw > st::maxMediaSize) {
th = (st::maxMediaSize * th) / tw;
tw = st::maxMediaSize;
}
auto minWidth = qMax(st::minPhotoSize, _parent->infoWidth() + 2 * (st::msgDateImgDelta + st::msgDateImgPadding.x()));
auto maxWidth = qMax(tw, minWidth);
auto minHeight = qMax(th, st::minPhotoSize);
if (_parent->hasBubble()) {
if (!_title.isEmpty()) {
minHeight += qMin(_title.countHeight(maxWidth - st::msgPadding.left() - st::msgPadding.right()), 2 * st::webPageTitleFont->height);
}
if (!_description.isEmpty()) {
minHeight += qMin(_description.countHeight(maxWidth - st::msgPadding.left() - st::msgPadding.right()), 3 * st::webPageDescriptionFont->height);
}
if (!_title.isEmpty() || !_description.isEmpty()) {
minHeight += st::mediaInBubbleSkip;
if (isBubbleTop()) {
minHeight += st::msgPadding.top();
}
}
}
return { maxWidth, minHeight };
}
QSize Location::countCurrentSize(int newWidth) {
accumulate_min(newWidth, maxWidth());
auto tw = fullWidth();
auto th = fullHeight();
if (tw > st::maxMediaSize) {
th = (st::maxMediaSize * th) / tw;
tw = st::maxMediaSize;
}
auto newHeight = th;
if (tw > newWidth) {
newHeight = (newWidth * newHeight / tw);
} else {
newWidth = tw;
}
auto minWidth = qMax(st::minPhotoSize, _parent->infoWidth() + 2 * (st::msgDateImgDelta + st::msgDateImgPadding.x()));
accumulate_max(newWidth, minWidth);
accumulate_max(newHeight, st::minPhotoSize);
if (_parent->hasBubble()) {
if (!_title.isEmpty()) {
newHeight += qMin(_title.countHeight(newWidth - st::msgPadding.left() - st::msgPadding.right()), st::webPageTitleFont->height * 2);
}
if (!_description.isEmpty()) {
newHeight += qMin(_description.countHeight(newWidth - st::msgPadding.left() - st::msgPadding.right()), st::webPageDescriptionFont->height * 3);
}
if (!_title.isEmpty() || !_description.isEmpty()) {
newHeight += st::mediaInBubbleSkip;
if (isBubbleTop()) {
newHeight += st::msgPadding.top();
}
}
}
return { newWidth, newHeight };
}
TextSelection Location::toDescriptionSelection(
TextSelection selection) const {
return UnshiftItemSelection(selection, _title);
}
TextSelection Location::fromDescriptionSelection(
TextSelection selection) const {
return ShiftItemSelection(selection, _title);
}
void Location::draw(Painter &p, const QRect &r, TextSelection selection, crl::time ms) const {
if (width() < st::msgPadding.left() + st::msgPadding.right() + 1) return;
auto paintx = 0, painty = 0, paintw = width(), painth = height();
bool bubble = _parent->hasBubble();
auto outbg = _parent->hasOutLayout();
bool selected = (selection == FullSelection);
if (bubble) {
if (!_title.isEmpty() || !_description.isEmpty()) {
if (isBubbleTop()) {
painty += st::msgPadding.top();
}
}
auto textw = width() - st::msgPadding.left() - st::msgPadding.right();
if (!_title.isEmpty()) {
p.setPen(outbg ? st::webPageTitleOutFg : st::webPageTitleInFg);
_title.drawLeftElided(p, paintx + st::msgPadding.left(), painty, textw, width(), 2, style::al_left, 0, -1, 0, false, selection);
painty += qMin(_title.countHeight(textw), 2 * st::webPageTitleFont->height);
}
if (!_description.isEmpty()) {
p.setPen(outbg ? st::webPageDescriptionOutFg : st::webPageDescriptionInFg);
_description.drawLeftElided(p, paintx + st::msgPadding.left(), painty, textw, width(), 3, style::al_left, 0, -1, 0, false, toDescriptionSelection(selection));
painty += qMin(_description.countHeight(textw), 3 * st::webPageDescriptionFont->height);
}
if (!_title.isEmpty() || !_description.isEmpty()) {
painty += st::mediaInBubbleSkip;
}
painth -= painty;
} else {
App::roundShadow(p, 0, 0, paintw, painth, selected ? st::msgInShadowSelected : st::msgInShadow, selected ? InSelectedShadowCorners : InShadowCorners);
}
const auto contextId = _parent->data()->fullId();
_data->load(contextId);
auto roundRadius = ImageRoundRadius::Large;
auto roundCorners = ((isBubbleTop() && _title.isEmpty() && _description.isEmpty()) ? (RectPart::TopLeft | RectPart::TopRight) : RectPart::None)
| (isBubbleBottom() ? (RectPart::BottomLeft | RectPart::BottomRight) : RectPart::None);
auto rthumb = QRect(paintx, painty, paintw, painth);
if (_data && !_data->thumb->isNull()) {
const auto &pix = _data->thumb->pixSingle(contextId, paintw, painth, paintw, painth, roundRadius, roundCorners);
p.drawPixmap(rthumb.topLeft(), pix);
} else {
App::complexLocationRect(p, rthumb, roundRadius, roundCorners);
}
const auto paintMarker = [&](const style::icon &icon) {
icon.paint(
p,
rthumb.x() + ((rthumb.width() - icon.width()) / 2),
rthumb.y() + (rthumb.height() / 2) - icon.height(),
width());
};
paintMarker(st::historyMapPoint);
paintMarker(st::historyMapPointInner);
if (selected) {
App::complexOverlayRect(p, rthumb, roundRadius, roundCorners);
}
if (_parent->media() == this) {
auto fullRight = paintx + paintw;
auto fullBottom = height();
_parent->drawInfo(p, fullRight, fullBottom, paintx * 2 + paintw, selected, InfoDisplayType::Image);
if (!bubble && _parent->displayRightAction()) {
auto fastShareLeft = (fullRight + st::historyFastShareLeft);
auto fastShareTop = (fullBottom - st::historyFastShareBottom - st::historyFastShareSize);
_parent->drawRightAction(p, fastShareLeft, fastShareTop, 2 * paintx + paintw);
}
}
}
TextState Location::textState(QPoint point, StateRequest request) const {
auto result = TextState(_parent);
auto symbolAdd = 0;
if (width() < st::msgPadding.left() + st::msgPadding.right() + 1) {
return result;
}
auto paintx = 0, painty = 0, paintw = width(), painth = height();
bool bubble = _parent->hasBubble();
if (bubble) {
if (!_title.isEmpty() || !_description.isEmpty()) {
if (isBubbleTop()) {
painty += st::msgPadding.top();
}
}
auto textw = width() - st::msgPadding.left() - st::msgPadding.right();
if (!_title.isEmpty()) {
auto titleh = qMin(_title.countHeight(textw), 2 * st::webPageTitleFont->height);
if (point.y() >= painty && point.y() < painty + titleh) {
result = TextState(_parent, _title.getStateLeft(
point - QPoint(paintx + st::msgPadding.left(), painty),
textw,
width(),
request.forText()));
return result;
} else if (point.y() >= painty + titleh) {
symbolAdd += _title.length();
}
painty += titleh;
}
if (!_description.isEmpty()) {
auto descriptionh = qMin(_description.countHeight(textw), 3 * st::webPageDescriptionFont->height);
if (point.y() >= painty && point.y() < painty + descriptionh) {
result = TextState(_parent, _description.getStateLeft(
point - QPoint(paintx + st::msgPadding.left(), painty),
textw,
width(),
request.forText()));
} else if (point.y() >= painty + descriptionh) {
symbolAdd += _description.length();
}
painty += descriptionh;
}
if (!_title.isEmpty() || !_description.isEmpty()) {
painty += st::mediaInBubbleSkip;
}
painth -= painty;
}
if (QRect(paintx, painty, paintw, painth).contains(point) && _data) {
result.link = _link;
}
if (_parent->media() == this) {
auto fullRight = paintx + paintw;
auto fullBottom = height();
if (_parent->pointInTime(fullRight, fullBottom, point, InfoDisplayType::Image)) {
result.cursor = CursorState::Date;
}
if (!bubble && _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();
}
}
}
result.symbol += symbolAdd;
return result;
}
TextSelection Location::adjustSelection(TextSelection selection, TextSelectType type) const {
if (_description.isEmpty() || selection.to <= _title.length()) {
return _title.adjustSelection(selection, type);
}
auto descriptionSelection = _description.adjustSelection(toDescriptionSelection(selection), type);
if (selection.from >= _title.length()) {
return fromDescriptionSelection(descriptionSelection);
}
auto titleSelection = _title.adjustSelection(selection, type);
return { titleSelection.from, fromDescriptionSelection(descriptionSelection).to };
}
TextForMimeData Location::selectedText(TextSelection selection) const {
auto titleResult = _title.toTextForMimeData(selection);
auto descriptionResult = _description.toTextForMimeData(
toDescriptionSelection(selection));
if (titleResult.empty()) {
return descriptionResult;
} else if (descriptionResult.empty()) {
return titleResult;
}
return titleResult.append('\n').append(std::move(descriptionResult));
}
bool Location::needsBubble() const {
if (!_title.isEmpty() || !_description.isEmpty()) {
return true;
}
const auto item = _parent->data();
return item->viaBot()
|| item->Has<HistoryMessageReply>()
|| _parent->displayForwardedFrom()
|| _parent->displayFromName();
return false;
}
int Location::fullWidth() const {
return st::locationSize.width();
}
int Location::fullHeight() const {
return st::locationSize.height();
}
} // namespace HistoryView