Implement live location view.

This commit is contained in:
John Preston 2024-04-30 14:58:32 +04:00
parent ef8c07e6eb
commit b9b7d9e337
15 changed files with 555 additions and 63 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 416 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 913 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

View file

@ -3092,6 +3092,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_unread_bar_some" = "Unread messages";
"lng_maps_point" = "Location";
"lng_live_location" = "Live Location";
"lng_live_location_now" = "updated just now";
"lng_live_location_minutes#one" = "updated {count} minute ago";
"lng_live_location_minutes#other" = "updated {count} minutes ago";
"lng_live_location_hours#one" = "updated {count} hour ago";
"lng_live_location_hours#other" = "updated {count} hours ago";
"lng_live_location_today" = "updated today at {time}";
"lng_live_location_yesterday" = "updated yesterday at {time}";
"lng_live_location_date_time" = "updated {date} at {time}";
"lng_save_photo" = "Save image";
"lng_save_video" = "Save video";
"lng_save_audio_file" = "Save audio file";

View file

@ -1311,8 +1311,9 @@ std::unique_ptr<HistoryView::Media> MediaContact::createView(
MediaLocation::MediaLocation(
not_null<HistoryItem*> parent,
const LocationPoint &point)
: MediaLocation(parent, point, QString(), QString()) {
const LocationPoint &point,
TimeId livePeriod)
: MediaLocation({}, parent, point, livePeriod, QString(), QString()) {
}
MediaLocation::MediaLocation(
@ -1320,17 +1321,30 @@ MediaLocation::MediaLocation(
const LocationPoint &point,
const QString &title,
const QString &description)
: MediaLocation({}, parent, point, TimeId(), title, description) {
}
MediaLocation::MediaLocation(
PrivateTag,
not_null<HistoryItem*> parent,
const LocationPoint &point,
TimeId livePeriod,
const QString &title,
const QString &description)
: Media(parent)
, _point(point)
, _location(parent->history()->owner().location(point))
, _livePeriod(livePeriod)
, _title(title)
, _description(description) {
}
std::unique_ptr<Media> MediaLocation::clone(not_null<HistoryItem*> parent) {
return std::make_unique<MediaLocation>(
PrivateTag(),
parent,
_point,
_livePeriod,
_title,
_description);
}
@ -1339,8 +1353,14 @@ CloudImage *MediaLocation::location() const {
return _location;
}
QString MediaLocation::typeString() const {
return _livePeriod
? tr::lng_live_location(tr::now)
: tr::lng_maps_point(tr::now);
}
ItemPreview MediaLocation::toPreview(ToPreviewOptions options) const {
const auto type = tr::lng_maps_point(tr::now);
const auto type = typeString();
const auto hasMiniImages = false;
const auto text = TextWithEntities{ .text = _title };
return {
@ -1349,9 +1369,7 @@ ItemPreview MediaLocation::toPreview(ToPreviewOptions options) const {
}
TextWithEntities MediaLocation::notificationText() const {
return WithCaptionNotificationText(
tr::lng_maps_point(tr::now),
{ .text = _title });
return WithCaptionNotificationText(typeString(), { .text = _title });
}
QString MediaLocation::pinnedTextSubstring() const {
@ -1360,7 +1378,7 @@ QString MediaLocation::pinnedTextSubstring() const {
TextForMimeData MediaLocation::clipboardText() const {
auto result = TextForMimeData::Simple(
u"[ "_q + tr::lng_maps_point(tr::now) + u" ]\n"_q);
u"[ "_q + typeString() + u" ]\n"_q);
auto titleResult = TextUtilities::ParseEntities(
_title,
Ui::WebpageTextTitleOptions().flags);
@ -1389,12 +1407,19 @@ std::unique_ptr<HistoryView::Media> MediaLocation::createView(
not_null<HistoryView::Element*> message,
not_null<HistoryItem*> realParent,
HistoryView::Element *replacing) {
return std::make_unique<HistoryView::Location>(
message,
_location,
_point,
_title,
_description);
return _livePeriod
? std::make_unique<HistoryView::Location>(
message,
_location,
_point,
replacing,
_livePeriod)
: std::make_unique<HistoryView::Location>(
message,
_location,
_point,
_title,
_description);
}
MediaCall::MediaCall(not_null<HistoryItem*> parent, const Call &call)

View file

@ -331,10 +331,14 @@ private:
};
class MediaLocation final : public Media {
struct PrivateTag {
};
public:
MediaLocation(
not_null<HistoryItem*> parent,
const LocationPoint &point);
const LocationPoint &point,
TimeId livePeriod = 0);
MediaLocation(
not_null<HistoryItem*> parent,
const LocationPoint &point,
@ -356,9 +360,21 @@ public:
not_null<HistoryItem*> realParent,
HistoryView::Element *replacing = nullptr) override;
MediaLocation(
PrivateTag,
not_null<HistoryItem*> parent,
const LocationPoint &point,
TimeId livePeriod,
const QString &title,
const QString &description);
private:
[[nodiscard]] QString typeString() const;
LocationPoint _point;
not_null<CloudImage*> _location;
TimeId _livePeriod = 0;
QString _title;
QString _description;

View file

@ -224,10 +224,12 @@ std::unique_ptr<Data::Media> HistoryItem::CreateMedia(
return nullptr;
});
}, [&](const MTPDmessageMediaGeoLive &media) -> Result {
const auto period = media.vperiod().v;
return media.vgeo().match([&](const MTPDgeoPoint &point) -> Result {
return std::make_unique<Data::MediaLocation>(
item,
Data::LocationPoint(point));
Data::LocationPoint(point),
media.vperiod().v);
}, [](const MTPDgeoPointEmpty &) -> Result {
return nullptr;
});

View file

@ -7,12 +7,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "history/view/media/history_view_location.h"
#include "base/unixtime.h"
#include "history/history.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 "lang/lang_keys.h"
#include "ui/chat/chat_style.h"
#include "ui/image/image.h"
#include "ui/text/text_options.h"
@ -24,6 +26,98 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "styles/style_chat.h"
namespace HistoryView {
namespace {
constexpr auto kUntilOffPeriod = std::numeric_limits<TimeId>::max();
constexpr auto kLiveElapsedPartOpacity = 0.2;
[[nodiscard]] TimeId ResolveUpdateDate(not_null<Element*> view) {
const auto item = view->data();
const auto edited = item->Get<HistoryMessageEdited>();
return edited ? edited->date : item->date();
}
[[nodiscard]] QString RemainingTimeText(
not_null<Element*> view,
TimeId period) {
if (period == kUntilOffPeriod) {
return QString(1, QChar(0x221E));
}
const auto elapsed = base::unixtime::now() - view->data()->date();
const auto remaining = std::clamp(period - elapsed, 0, period);
if (remaining < 10) {
return tr::lng_seconds_tiny(tr::now, lt_count, remaining);
} else if (remaining < 600) {
return tr::lng_minutes_tiny(tr::now, lt_count, remaining / 60);
} else if (remaining < 3600) {
return QString::number(remaining / 60);
} else if (remaining < 86400) {
return tr::lng_hours_tiny(tr::now, lt_count, remaining / 3600);
}
return tr::lng_days_tiny(tr::now, lt_count, remaining / 86400);
}
[[nodiscard]] float64 RemainingTimeProgress(
not_null<Element*> view,
TimeId period) {
if (period == kUntilOffPeriod) {
return 1.;
} else if (period < 1) {
return 0.;
}
const auto elapsed = base::unixtime::now() - view->data()->date();
return std::clamp(period - elapsed, 0, period) / float64(period);
}
} // namespace
struct Location::Live {
explicit Live(TimeId period) : period(period) {
}
base::Timer updateStatusTimer;
base::Timer updateRemainingTimer;
QImage previous;
QImage previousCache;
Ui::BubbleRounding previousRounding;
Ui::Animations::Simple crossfade;
TimeId period = 0;
int thumbnailHeight = 0;
};
Location::Location(
not_null<Element*> parent,
not_null<Data::CloudImage*> data,
Data::LocationPoint point,
Element *replacing,
TimeId livePeriod)
: Media(parent)
, _data(data)
, _live(CreateLiveTracker(parent, livePeriod))
, _title(st::msgMinWidth)
, _description(st::msgMinWidth)
, _link(std::make_shared<LocationClickHandler>(point)) {
if (_live) {
_title.setText(
st::webPageTitleStyle,
tr::lng_live_location(tr::now),
Ui::WebpageTextTitleOptions());
_live->updateStatusTimer.setCallback([=] {
updateLiveStatus();
checkLiveFinish();
});
_live->updateRemainingTimer.setCallback([=] {
checkLiveFinish();
});
updateLiveStatus();
if (const auto media = replacing ? replacing->media() : nullptr) {
_live->previous = media->locationTakeImage();
if (!_live->previous.isNull()) {
history()->owner().registerHeavyViewPart(_parent);
}
}
}
}
Location::Location(
not_null<Element*> parent,
@ -53,18 +147,106 @@ Location::Location(
}
Location::~Location() {
if (_media) {
_media = nullptr;
if (hasHeavyPart()) {
unloadHeavyPart();
_parent->checkHeavyPart();
}
}
void Location::checkLiveFinish() {
Expects(_live != nullptr);
const auto now = base::unixtime::now();
const auto item = _parent->data();
const auto start = item->date();
if (_live->period != kUntilOffPeriod && now - start >= _live->period) {
_live = nullptr;
item->history()->owner().requestViewResize(_parent);
} else {
_parent->repaint();
}
}
std::unique_ptr<Location::Live> Location::CreateLiveTracker(
not_null<Element*> parent,
TimeId period) {
if (!period) {
return nullptr;
}
const auto now = base::unixtime::now();
const auto date = parent->data()->date();
return (now < date || now - date < period)
? std::make_unique<Live>(period)
: nullptr;
}
void Location::updateLiveStatus() {
const auto date = ResolveUpdateDate(_parent);
const auto now = base::unixtime::now();
const auto elapsed = now - date;
auto next = TimeId();
const auto text = [&] {
if (elapsed < 60) {
next = 60 - elapsed;
return tr::lng_live_location_now(tr::now);
} else if (const auto minutes = elapsed / 60; minutes < 60) {
next = 60 - (elapsed % 60);
return tr::lng_live_location_minutes(tr::now, lt_count, minutes);
} else if (const auto hours = elapsed / 3600; hours < 12) {
next = 3600 - (elapsed % 3600);
return tr::lng_live_location_hours(tr::now, lt_count, hours);
}
const auto dateFull = base::unixtime::parse(date);
const auto nowFull = base::unixtime::parse(now);
const auto nextTomorrow = [&] {
const auto tomorrow = nowFull.date().addDays(1);
next = nowFull.secsTo(QDateTime(tomorrow, QTime(0, 0)));
};
const auto locale = QLocale();
const auto format = QLocale::ShortFormat;
if (dateFull.date() == nowFull.date()) {
nextTomorrow();
const auto time = locale.toString(dateFull.time(), format);
return tr::lng_live_location_today(tr::now, lt_time, time);
} else if (dateFull.date().addDays(1) == nowFull.date()) {
nextTomorrow();
const auto time = locale.toString(dateFull.time(), format);
return tr::lng_live_location_yesterday(tr::now, lt_time, time);
}
return tr::lng_live_location_date_time(
tr::now,
lt_date,
locale.toString(dateFull.date(), format),
lt_time,
locale.toString(dateFull.time(), format));
}();
_description.setMarkedText(
st::webPageDescriptionStyle,
{ text },
Ui::WebpageTextDescriptionOptions());
if (next > 0 && next < 86400) {
_live->updateStatusTimer.callOnce(next * crl::time(1000));
}
}
QImage Location::locationTakeImage() {
if (_media && !_media->isNull()) {
return *_media;
} else if (_live && !_live->previous.isNull()) {
return _live->previous;
}
return {};
}
void Location::unloadHeavyPart() {
_media = nullptr;
if (_live) {
_live->previous = QImage();
}
}
bool Location::hasHeavyPart() const {
return (_media != nullptr);
return (_media != nullptr) || (_live && !_live->previous.isNull());
}
void Location::ensureMediaCreated() const {
@ -99,8 +281,14 @@ QSize Location::countOptimalSize() {
}
if (!_title.isEmpty() || !_description.isEmpty()) {
minHeight += st::mediaInBubbleSkip;
if (isBubbleTop()) {
minHeight += st::msgPadding.top();
if (_live) {
if (isBubbleBottom()) {
minHeight += st::msgPadding.bottom();
}
} else {
if (isBubbleTop()) {
minHeight += st::msgPadding.top();
}
}
}
}
@ -128,6 +316,9 @@ QSize Location::countCurrentSize(int newWidth) {
std::min(newWidth, st::maxMediaSize));
accumulate_max(newWidth, minWidth);
accumulate_max(newHeight, st::minPhotoSize);
if (_live) {
_live->thumbnailHeight = newHeight;
}
if (_parent->hasBubble()) {
if (!_title.isEmpty()) {
newHeight += qMin(_title.countHeight(newWidth - st::msgPadding.left() - st::msgPadding.right()), st::webPageTitleFont->height * 2);
@ -137,8 +328,14 @@ QSize Location::countCurrentSize(int newWidth) {
}
if (!_title.isEmpty() || !_description.isEmpty()) {
newHeight += st::mediaInBubbleSkip;
if (isBubbleTop()) {
newHeight += st::msgPadding.top();
if (_live) {
if (isBubbleBottom()) {
newHeight += st::msgPadding.bottom();
}
} else {
if (isBubbleTop()) {
newHeight += st::msgPadding.top();
}
}
}
}
@ -156,20 +353,27 @@ TextSelection Location::fromDescriptionSelection(
}
void Location::draw(Painter &p, const PaintContext &context) const {
if (width() < st::msgPadding.left() + st::msgPadding.right() + 1) return;
if (width() < st::msgPadding.left() + st::msgPadding.right() + 1) {
return;
}
auto paintx = 0, painty = 0, paintw = width(), painth = height();
bool bubble = _parent->hasBubble();
const auto st = context.st;
const auto stm = context.messageStyle();
const auto hasText = !_title.isEmpty() || !_description.isEmpty();
const auto rounding = adjustedBubbleRounding(
hasText ? RectPart::FullTop : RectPart());
if (bubble) {
if (hasText) {
if (isBubbleTop()) {
painty += st::msgPadding.top();
}
const auto rounding = adjustedBubbleRounding(_live
? RectPart::FullBottom
: hasText
? RectPart::FullTop
: RectPart());
const auto paintText = [&] {
if (_live) {
painty += st::mediaInBubbleSkip;
} else if (!hasText) {
return;
} else if (isBubbleTop()) {
painty += st::msgPadding.top();
}
auto textw = width() - st::msgPadding.left() - st::msgPadding.right();
@ -180,24 +384,45 @@ void Location::draw(Painter &p, const PaintContext &context) const {
painty += qMin(_title.countHeight(textw), 2 * st::webPageTitleFont->height);
}
if (!_description.isEmpty()) {
if (_live) {
p.setPen(stm->msgDateFg);
}
_description.drawLeftElided(p, paintx + st::msgPadding.left(), painty, textw, width(), 3, style::al_left, 0, -1, 0, false, toDescriptionSelection(context.selection));
painty += qMin(_description.countHeight(textw), 3 * st::webPageDescriptionFont->height);
}
if (!_title.isEmpty() || !_description.isEmpty()) {
if (!_live) {
painty += st::mediaInBubbleSkip;
painth -= painty;
}
painth -= painty;
};
if (!_live) {
paintText();
}
auto rthumb = QRect(paintx, painty, paintw, painth);
const auto thumbh = _live ? _live->thumbnailHeight : painth;
auto rthumb = QRect(paintx, painty, paintw, thumbh);
if (!bubble) {
fillImageShadow(p, rthumb, rounding, context);
}
ensureMediaCreated();
validateImageCache(rthumb.size(), rounding);
if (!_imageCache.isNull()) {
const auto paintPrevious = _live && !_live->previous.isNull();
auto opacity = _imageCache.isNull() ? 0. : 1.;
if (paintPrevious) {
opacity = _live->crossfade.value(opacity);
if (opacity < 1.) {
p.drawImage(rthumb.topLeft(), _live->previousCache);
if (opacity > 0.) {
p.setOpacity(opacity);
}
}
}
if (!_imageCache.isNull() && opacity > 0.) {
p.drawImage(rthumb.topLeft(), _imageCache);
} else if (!bubble) {
if (opacity < 1.) {
p.setOpacity(1.);
}
} else if (!bubble && !paintPrevious) {
Ui::PaintBubble(
p,
Ui::SimpleBubble{
@ -223,8 +448,12 @@ void Location::draw(Painter &p, const PaintContext &context) const {
if (context.selected()) {
fillImageOverlay(p, rthumb, rounding, context);
}
if (_parent->media() == this) {
if (_live) {
painty += _live->thumbnailHeight;
painth -= _live->thumbnailHeight;
paintLiveRemaining(p, context, { paintx, painty, paintw, painth });
paintText();
} else if (_parent->media() == this) {
auto fullRight = paintx + paintw;
auto fullBottom = height();
_parent->drawInfo(
@ -244,25 +473,114 @@ void Location::draw(Painter &p, const PaintContext &context) const {
}
}
void Location::paintLiveRemaining(
QPainter &p,
const PaintContext &context,
QRect bottom) const {
const auto size = st::liveLocationRemainingSize;
const auto skip = (bottom.height() - size) / 2;
const auto rect = QRect(
bottom.x() + bottom.width() - size - skip,
bottom.y() + skip,
size,
size);
auto hq = PainterHighQualityEnabler(p);
const auto stm = context.messageStyle();
const auto color = stm->msgServiceFg->c;
const auto untiloff = (_live->period == kUntilOffPeriod);
const auto progress = RemainingTimeProgress(_parent, _live->period);
const auto part = 1. / 360;
const auto full = (progress >= 1. - part);
auto elapsed = color;
if (!full) {
elapsed.setAlphaF(elapsed.alphaF() * kLiveElapsedPartOpacity);
}
auto pen = QPen(elapsed);
const auto stroke = style::ConvertScaleExact(2.);
pen.setWidthF(stroke);
p.setPen(pen);
p.setBrush(Qt::NoBrush);
p.drawEllipse(rect);
if (untiloff) {
stm->liveLocationLongIcon.paintInCenter(p, rect);
} else {
if (!full && progress > part) {
auto pen = QPen(color);
pen.setWidthF(stroke);
p.setPen(pen);
p.drawArc(rect, 90 * 16, int(base::SafeRound(360 * 16 * progress)));
}
p.setPen(stm->msgServiceFg);
p.setFont(st::semiboldFont);
const auto text = RemainingTimeText(_parent, _live->period);
p.drawText(rect, text, style::al_center);
const auto each = std::clamp(_live->period / 360, 1, 86400);
_live->updateRemainingTimer.callOnce(each * crl::time(1000));
}
}
void Location::validateImageCache(
QSize outer,
Ui::BubbleRounding rounding) const {
Expects(_media != nullptr);
const auto ratio = style::DevicePixelRatio();
if ((_imageCache.size() == (outer * ratio)
&& _imageCacheRounding == rounding)
|| _media->isNull()) {
if (_live && !_live->previous.isNull()) {
validateImageCache(
_live->previous,
_live->previousCache,
_live->previousRounding,
outer,
rounding);
}
validateImageCache(
*_media,
_imageCache,
_imageCacheRounding,
outer,
rounding);
checkLiveCrossfadeStart();
}
void Location::checkLiveCrossfadeStart() const {
if (!_live
|| _live->previous.isNull()
|| !_media
|| _media->isNull()
|| _live->crossfade.animating()) {
return;
}
_imageCache = Images::Round(
_media->scaled(
_live->crossfade.start([=] {
if (!_live->crossfade.animating()) {
_live->previous = QImage();
_live->previousCache = QImage();
}
_parent->repaint();
}, 0., 1., st::fadeWrapDuration);
}
void Location::validateImageCache(
const QImage &source,
QImage &cache,
Ui::BubbleRounding &cacheRounding,
QSize outer,
Ui::BubbleRounding rounding) const {
if (source.isNull()) {
return;
}
const auto ratio = style::DevicePixelRatio();
if (cache.size() == (outer * ratio) && cacheRounding == rounding) {
return;
}
cache = Images::Round(
source.scaled(
outer * ratio,
Qt::IgnoreAspectRatio,
Qt::SmoothTransformation),
MediaRoundingMask(rounding));
_imageCache.setDevicePixelRatio(ratio);
_imageCacheRounding = rounding;
cache.setDevicePixelRatio(ratio);
cacheRounding = rounding;
}
TextState Location::textState(QPoint point, StateRequest request) const {
@ -275,11 +593,13 @@ TextState Location::textState(QPoint point, StateRequest request) const {
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 checkText = [&] {
if (_live) {
painty += st::mediaInBubbleSkip;
} else if (_title.isEmpty() && _description.isEmpty()) {
return false;
} else if (isBubbleTop()) {
painty += st::msgPadding.top();
}
auto textw = width() - st::msgPadding.left() - st::msgPadding.right();
@ -292,7 +612,7 @@ TextState Location::textState(QPoint point, StateRequest request) const {
textw,
width(),
request.forText()));
return result;
return true;
} else if (point.y() >= painty + titleh) {
symbolAdd += _title.length();
}
@ -306,6 +626,8 @@ TextState Location::textState(QPoint point, StateRequest request) const {
textw,
width(),
request.forText()));
result.symbol += symbolAdd;
return true;
} else if (point.y() >= painty + descriptionh) {
symbolAdd += _description.length();
}
@ -315,11 +637,22 @@ TextState Location::textState(QPoint point, StateRequest request) const {
painty += st::mediaInBubbleSkip;
}
painth -= painty;
return false;
};
if (!_live && checkText()) {
return result;
}
if (QRect(paintx, painty, paintw, painth).contains(point) && _data) {
const auto thumbh = _live ? _live->thumbnailHeight : painth;
if (QRect(paintx, painty, paintw, thumbh).contains(point) && _data) {
result.link = _link;
}
if (_parent->media() == this) {
if (_live) {
painty += _live->thumbnailHeight;
painth -= _live->thumbnailHeight;
if (checkText()) {
return result;
}
} else if (_parent->media() == this) {
auto fullRight = paintx + paintw;
auto fullBottom = height();
const auto bottomInfoResult = _parent->bottomInfoTextState(

View file

@ -22,8 +22,14 @@ public:
not_null<Element*> parent,
not_null<Data::CloudImage*> data,
Data::LocationPoint point,
const QString &title = QString(),
const QString &description = QString());
Element *replacing = nullptr,
TimeId livePeriod = 0);
Location(
not_null<Element*> parent,
not_null<Data::CloudImage*> data,
Data::LocationPoint point,
const QString &title,
const QString &description);
~Location();
void draw(Painter &p, const PaintContext &context) const override;
@ -58,15 +64,33 @@ public:
return isRoundedInBubbleBottom();
}
QImage locationTakeImage() override;
void unloadHeavyPart() override;
bool hasHeavyPart() const override;
private:
struct Live;
[[nodiscard]] static std::unique_ptr<Live> CreateLiveTracker(
not_null<Element*> parent,
TimeId period);
void ensureMediaCreated() const;
void validateImageCache(
QSize outer,
Ui::BubbleRounding rounding) const;
void validateImageCache(
const QImage &source,
QImage &cache,
Ui::BubbleRounding &cacheRounding,
QSize outer,
Ui::BubbleRounding rounding) const;
void paintLiveRemaining(
QPainter &p,
const PaintContext &context,
QRect bottom) const;
QSize countOptimalSize() override;
QSize countCurrentSize(int newWidth) override;
@ -79,7 +103,12 @@ private:
[[nodiscard]] int fullWidth() const;
[[nodiscard]] int fullHeight() const;
void checkLiveCrossfadeStart() const;
void updateLiveStatus();
void checkLiveFinish();
const not_null<Data::CloudImage*> _data;
mutable std::unique_ptr<Live> _live;
mutable std::shared_ptr<QImage> _media;
Ui::Text::String _title, _description;
ClickHandlerPtr _link;

View file

@ -356,6 +356,10 @@ std::unique_ptr<StickerPlayer> Media::stickerTakePlayer(
return nullptr;
}
QImage Media::locationTakeImage() {
return QImage();
}
TextState Media::getStateGrouped(
const QRect &geometry,
RectParts sides,

View file

@ -190,6 +190,7 @@ public:
virtual std::unique_ptr<StickerPlayer> stickerTakePlayer(
not_null<DocumentData*> data,
const Lottie::ColorReplacements *replacements);
virtual QImage locationTakeImage();
virtual void checkAnimation() {
}

View file

@ -1070,3 +1070,9 @@ chatIntroWidth: 224px;
chatIntroTitleMargin: margins(11px, 16px, 11px, 4px);
chatIntroMargin: margins(11px, 0px, 11px, 0px);
chatIntroStickerPadding: margins(10px, 8px, 10px, 16px);
liveLocationLongInIcon: icon {{ "chat/live_location_long", msgInServiceFg }};
liveLocationLongInIconSelected: icon {{ "chat/live_location_long", msgInServiceFgSelected }};
liveLocationLongOutIcon: icon {{ "chat/live_location_long", msgOutServiceFg }};
liveLocationLongOutIconSelected: icon {{ "chat/live_location_long", msgOutServiceFgSelected }};
liveLocationRemainingSize: 28px;

View file

@ -543,6 +543,12 @@ ChatStyle::ChatStyle(rpl::producer<ColorIndicesCompressed> colorIndices) {
st::historyVoiceMessageInTTLSelected,
st::historyVoiceMessageOutTTL,
st::historyVoiceMessageOutTTLSelected);
make(
&MessageStyle::liveLocationLongIcon,
st::liveLocationLongInIcon,
st::liveLocationLongInIconSelected,
st::liveLocationLongOutIcon,
st::liveLocationLongOutIconSelected);
updateDarkValue();
}

View file

@ -92,6 +92,7 @@ struct MessageStyle {
style::icon historyTranscribeLock = { Qt::Uninitialized };
style::icon historyTranscribeHide = { Qt::Uninitialized };
style::icon historyVoiceMessageTTL = { Qt::Uninitialized };
style::icon liveLocationLongIcon = { Qt::Uninitialized };
std::array<
std::unique_ptr<Text::QuotePaintCache>,
kColorPatternsCount> quoteCache;

View file

@ -1510,22 +1510,82 @@ mac:
""")
if buildQt6:
stage('qt_6_2_8', """
qt6version = '6.2.8' if mac else '6.7.0'
qt6version_ = '6_2_8' if mac else '6_7_0'
arg0 = 'qt_' + qt6version_
arg1 = qt6version
stage('qt_' + qt6version_, """
win:
git clone -b v{1} https://github.com/qt/qt5.git {0}
mac:
git clone -b v6.2.8-lts-lgpl https://github.com/qt/qt5.git qt_6_2_8
cd qt_6_2_8
git clone -b v{1}-lts-lgpl https://github.com/qt/qt5.git {0}
common:
cd {0}
git submodule update --init --recursive qtbase qtimageformats qtsvg
depends:patches/qtbase_6.2.8/*.patch
depends:patches/qtbase_{1}/*.patch
cd qtbase
find ../../patches/qtbase_6.2.8 -type f -print0 | sort -z | xargs -0 git apply -v
win:
for /r %%i in (..\\..\\patches\\qtbase_{1}\\*) do git apply %%i -v
cd ..
sed -i.bak 's/tqtc-//' {qtimageformats,qtsvg}/dependencies.yaml
SET CONFIGURATIONS=-debug
release:
SET CONFIGURATIONS=-debug-and-release
win:
""".format(arg0, arg1) + removeDir("\"%LIBS_DIR%\\Qt-\"" + qt6version) + """
SET ANGLE_DIR=%LIBS_DIR%\\tg_angle
SET ANGLE_LIBS_DIR=%ANGLE_DIR%\\out
SET MOZJPEG_DIR=%LIBS_DIR%\\mozjpeg
SET OPENSSL_DIR=%LIBS_DIR%\\openssl3
SET OPENSSL_LIBS_DIR=%OPENSSL_DIR%\\out
SET ZLIB_LIBS_DIR=%LIBS_DIR%\\zlib
SET WEBP_DIR=%LIBS_DIR%\\libwebp
configure -prefix "%LIBS_DIR%\\Qt-{1}" ^
%CONFIGURATIONS% ^
-force-debug-info ^
-opensource ^
-confirm-license ^
-static ^
-static-runtime ^
-opengl es2 -no-angle ^
-I "%ANGLE_DIR%\\include" ^
-D "KHRONOS_STATIC=" ^
-D "DESKTOP_APP_QT_STATIC_ANGLE=" ^
QMAKE_LIBS_OPENGL_ES2_DEBUG="%ANGLE_LIBS_DIR%\\Debug\\tg_angle.lib %ZLIB_LIBS_DIR%\\Debug\\zlibstaticd.lib d3d9.lib dxgi.lib dxguid.lib" ^
QMAKE_LIBS_OPENGL_ES2_RELEASE="%ANGLE_LIBS_DIR%\\Release\\tg_angle.lib %ZLIB_LIBS_DIR%\\Release\\zlibstatic.lib d3d9.lib dxgi.lib dxguid.lib" ^
-egl ^
QMAKE_LIBS_EGL_DEBUG="%ANGLE_LIBS_DIR%\\Debug\\tg_angle.lib %ZLIB_LIBS_DIR%\\Debug\\zlibstaticd.lib d3d9.lib dxgi.lib dxguid.lib Gdi32.lib User32.lib" ^
QMAKE_LIBS_EGL_RELEASE="%ANGLE_LIBS_DIR%\\Release\\tg_angle.lib %ZLIB_LIBS_DIR%\\Release\\zlibstatic.lib d3d9.lib dxgi.lib dxguid.lib Gdi32.lib User32.lib" ^
-openssl-linked ^
-I "%OPENSSL_DIR%\\include" ^
OPENSSL_LIBS_DEBUG="%OPENSSL_LIBS_DIR%.dbg\\libssl.lib %OPENSSL_LIBS_DIR%.dbg\\libcrypto.lib Ws2_32.lib Gdi32.lib Advapi32.lib Crypt32.lib User32.lib" ^
OPENSSL_LIBS_RELEASE="%OPENSSL_LIBS_DIR%\\libssl.lib %OPENSSL_LIBS_DIR%\\libcrypto.lib Ws2_32.lib Gdi32.lib Advapi32.lib Crypt32.lib User32.lib" ^
-I "%MOZJPEG_DIR%" ^
LIBJPEG_LIBS_DEBUG="%MOZJPEG_DIR%\\Debug\\jpeg-static.lib" ^
LIBJPEG_LIBS_RELEASE="%MOZJPEG_DIR%\\Release\\jpeg-static.lib" ^
-system-webp ^
-I "%WEBP_DIR%\\src" ^
-L "%WEBP_DIR%\\out\\release-static\\$X8664\\lib" ^
-mp ^
-no-feature-netlistmgr ^
-nomake examples ^
-nomake tests ^
-platform win32-msvc
jom -j%NUMBER_OF_PROCESSORS%
jom -j%NUMBER_OF_PROCESSORS% install
mac:
find ../../patches/qtbase_{1} -type f -print0 | sort -z | xargs -0 git apply -v
cd ..
sed -i.bak 's/tqtc-//' {{qtimageformats,qtsvg}}/dependencies.yaml
CONFIGURATIONS=-debug
release:
CONFIGURATIONS=-debug-and-release
mac:
./configure -prefix "$USED_PREFIX/Qt-6.2.8" \
./configure -prefix "$USED_PREFIX/Qt-{1}" \
$CONFIGURATIONS \
-force-debug-info \
-opensource \
@ -1546,7 +1606,7 @@ mac:
ninja
ninja install
""")
""".format(arg0, arg1))
stage('tg_owt', """
git clone https://github.com/desktop-app/tg_owt.git