| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354 |
- /*
- 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 "overview/overview_layout.h"
- #include "overview/overview_layout_delegate.h"
- #include "core/ui_integration.h" // TextContext
- #include "data/data_document.h"
- #include "data/data_document_resolver.h"
- #include "data/data_session.h"
- #include "data/data_web_page.h"
- #include "data/data_peer.h"
- #include "data/data_photo_media.h"
- #include "data/data_document_media.h"
- #include "data/data_file_click_handler.h"
- #include "ui/boxes/confirm_box.h"
- #include "lang/lang_keys.h"
- #include "layout/layout_selection.h"
- #include "storage/file_upload.h"
- #include "main/main_session.h"
- #include "media/audio/media_audio.h"
- #include "media/player/media_player_instance.h"
- #include "storage/localstorage.h"
- #include "history/history.h"
- #include "history/history_item.h"
- #include "history/history_item_components.h"
- #include "history/history_item_helpers.h"
- #include "history/view/history_view_cursor_state.h"
- #include "history/view/media/history_view_document.h" // DrawThumbnailAsSongCover
- #include "base/unixtime.h"
- #include "ui/effects/round_checkbox.h"
- #include "ui/effects/spoiler_mess.h"
- #include "ui/image/image.h"
- #include "ui/text/format_song_document_name.h"
- #include "ui/text/format_values.h"
- #include "ui/text/text_options.h"
- #include "ui/text/text_utilities.h"
- #include "ui/cached_round_corners.h"
- #include "ui/painter.h"
- #include "ui/power_saving.h"
- #include "ui/ui_utility.h"
- #include "styles/style_chat.h"
- #include "styles/style_chat_helpers.h"
- #include "styles/style_overview.h"
- namespace Overview {
- namespace Layout {
- namespace {
- using TextState = HistoryView::TextState;
- TextParseOptions _documentNameOptions = {
- TextParseMultiline | TextParseLinks | TextParseMarkdown, // flags
- 0, // maxw
- 0, // maxh
- Qt::LayoutDirectionAuto, // dir
- };
- constexpr auto kMaxInlineArea = 1280 * 720;
- constexpr auto kStoryRatio = 1.46;
- [[nodiscard]] bool CanPlayInline(not_null<DocumentData*> document) {
- const auto dimensions = document->dimensions;
- return dimensions.width() * dimensions.height() <= kMaxInlineArea;
- }
- [[nodiscard]] QImage CropMediaFrame(QImage image, int width, int height) {
- const auto ratio = style::DevicePixelRatio();
- width *= ratio;
- height *= ratio;
- const auto finalize = [&](QImage result) {
- result = result.scaled(
- width,
- height,
- Qt::IgnoreAspectRatio,
- Qt::SmoothTransformation);
- result.setDevicePixelRatio(ratio);
- return result;
- };
- if (image.width() * height == image.height() * width) {
- if (image.width() != width) {
- return finalize(std::move(image));
- }
- image.setDevicePixelRatio(ratio);
- return image;
- } else if (image.width() * height > image.height() * width) {
- const auto use = (image.height() * width) / height;
- const auto skip = (image.width() - use) / 2;
- return finalize(image.copy(skip, 0, use, image.height()));
- } else {
- const auto use = (image.width() * height) / width;
- const auto skip = (image.height() - use) / 2;
- return finalize(image.copy(0, skip, image.width(), use));
- }
- }
- } // namespace
- class Checkbox {
- public:
- template <typename UpdateCallback>
- Checkbox(UpdateCallback callback, const style::RoundCheckbox &st)
- : _updateCallback(callback)
- , _check(st, _updateCallback) {
- }
- void paint(Painter &p, QPoint position, int outerWidth, bool selected, bool selecting);
- void setActive(bool active);
- void setPressed(bool pressed);
- void invalidateCache() {
- _check.invalidateCache();
- }
- private:
- void startAnimation();
- Fn<void()> _updateCallback;
- Ui::RoundCheckbox _check;
- Ui::Animations::Simple _pression;
- bool _active = false;
- bool _pressed = false;
- };
- void Checkbox::paint(Painter &p, QPoint position, int outerWidth, bool selected, bool selecting) {
- _check.setDisplayInactive(selecting);
- _check.setChecked(selected);
- const auto pression = _pression.value((_active && _pressed) ? 1. : 0.);
- const auto masterScale = 1. - (1. - st::overviewCheckPressedSize) * pression;
- _check.paint(p, position.x(), position.y(), outerWidth, masterScale);
- }
- void Checkbox::setActive(bool active) {
- _active = active;
- if (_pressed) {
- startAnimation();
- }
- }
- void Checkbox::setPressed(bool pressed) {
- _pressed = pressed;
- if (_active) {
- startAnimation();
- }
- }
- void Checkbox::startAnimation() {
- auto showPressed = (_pressed && _active);
- _pression.start(_updateCallback, showPressed ? 0. : 1., showPressed ? 1. : 0., st::overviewCheck.duration);
- }
- ItemBase::ItemBase(
- not_null<Delegate*> delegate,
- not_null<HistoryItem*> parent)
- : _delegate(delegate)
- , _parent(parent)
- , _dateTime(ItemDateTime(parent)) {
- }
- ItemBase::~ItemBase() = default;
- QDateTime ItemBase::dateTime() const {
- return _dateTime;
- }
- void ItemBase::clickHandlerActiveChanged(
- const ClickHandlerPtr &action,
- bool active) {
- _parent->history()->session().data().requestItemRepaint(_parent);
- if (_check) {
- _check->setActive(active);
- }
- }
- void ItemBase::clickHandlerPressedChanged(
- const ClickHandlerPtr &action,
- bool pressed) {
- _parent->history()->session().data().requestItemRepaint(_parent);
- if (_check) {
- _check->setPressed(pressed);
- }
- }
- void ItemBase::invalidateCache() {
- if (_check) {
- _check->invalidateCache();
- }
- }
- void ItemBase::paintCheckbox(
- Painter &p,
- QPoint position,
- bool selected,
- const PaintContext *context) {
- if (selected || context->selecting) {
- ensureCheckboxCreated();
- }
- if (_check) {
- _check->paint(p, position, _width, selected, context->selecting);
- }
- }
- const style::RoundCheckbox &ItemBase::checkboxStyle() const {
- return st::overviewCheck;
- }
- void ItemBase::ensureCheckboxCreated() {
- if (_check) {
- return;
- }
- const auto repaint = [=] {
- _parent->history()->session().data().requestItemRepaint(_parent);
- };
- _check = std::make_unique<Checkbox>(repaint, checkboxStyle());
- }
- void RadialProgressItem::setDocumentLinks(
- not_null<DocumentData*> document,
- bool forceOpen) {
- const auto context = parent()->fullId();
- setLinks(
- std::make_shared<DocumentOpenClickHandler>(
- document,
- crl::guard(this, [=](FullMsgId id) {
- clearSpoiler();
- delegate()->openDocument(document, id, forceOpen);
- }),
- context),
- std::make_shared<DocumentSaveClickHandler>(document, context),
- std::make_shared<DocumentCancelClickHandler>(
- document,
- nullptr,
- context));
- }
- void RadialProgressItem::clickHandlerActiveChanged(
- const ClickHandlerPtr &action,
- bool active) {
- ItemBase::clickHandlerActiveChanged(action, active);
- if (action == _openl || action == _savel || action == _cancell) {
- if (iconAnimated()) {
- const auto repaint = [=] {
- parent()->history()->session().data().requestItemRepaint(
- parent());
- };
- _a_iconOver.start(
- repaint,
- active ? 0. : 1.,
- active ? 1. : 0.,
- st::msgFileOverDuration);
- }
- }
- }
- void RadialProgressItem::setLinks(
- ClickHandlerPtr &&openl,
- ClickHandlerPtr &&savel,
- ClickHandlerPtr &&cancell) {
- _openl = std::move(openl);
- _savel = std::move(savel);
- _cancell = std::move(cancell);
- }
- void RadialProgressItem::radialAnimationCallback(crl::time now) const {
- const auto updated = [&] {
- return _radial->update(dataProgress(), dataFinished(), now);
- }();
- if (!anim::Disabled() || updated) {
- parent()->history()->session().data().requestItemRepaint(parent());
- }
- if (!_radial->animating()) {
- checkRadialFinished();
- }
- }
- void RadialProgressItem::ensureRadial() {
- if (_radial) {
- return;
- }
- _radial = std::make_unique<Ui::RadialAnimation>([=](crl::time now) {
- radialAnimationCallback(now);
- });
- }
- void RadialProgressItem::checkRadialFinished() const {
- if (_radial && !_radial->animating() && dataLoaded()) {
- _radial.reset();
- }
- }
- RadialProgressItem::~RadialProgressItem() = default;
- void StatusText::update(
- int64 newSize,
- int64 fullSize,
- TimeId duration,
- TimeId realDuration) {
- setSize(newSize);
- if (_size == Ui::FileStatusSizeReady) {
- _text = (duration >= 0) ? Ui::FormatDurationAndSizeText(duration, fullSize) : (duration < -1 ? Ui::FormatGifAndSizeText(fullSize) : Ui::FormatSizeText(fullSize));
- } else if (_size == Ui::FileStatusSizeLoaded) {
- _text = (duration >= 0) ? Ui::FormatDurationText(duration) : (duration < -1 ? u"GIF"_q : Ui::FormatSizeText(fullSize));
- } else if (_size == Ui::FileStatusSizeFailed) {
- _text = tr::lng_attach_failed(tr::now);
- } else if (_size >= 0) {
- _text = Ui::FormatDownloadText(_size, fullSize);
- } else {
- _text = Ui::FormatPlayedText(-_size - 1, realDuration);
- }
- }
- void StatusText::setSize(int64 newSize) {
- _size = newSize;
- }
- Photo::Photo(
- not_null<Delegate*> delegate,
- not_null<HistoryItem*> parent,
- not_null<PhotoData*> photo,
- MediaOptions options)
- : ItemBase(delegate, parent)
- , _data(photo)
- , _link(std::make_shared<PhotoOpenClickHandler>(
- photo,
- crl::guard(this, [=](FullMsgId id) {
- clearSpoiler();
- delegate->openPhoto(photo, id);
- }),
- parent->fullId()))
- , _spoiler(options.spoiler ? std::make_unique<Ui::SpoilerAnimation>([=] {
- delegate->repaintItem(this);
- }) : nullptr)
- , _pinned(options.pinned)
- , _story(options.story) {
- if (_data->inlineThumbnailBytes().isEmpty()
- && (_data->hasExact(Data::PhotoSize::Small)
- || _data->hasExact(Data::PhotoSize::Thumbnail))) {
- _data->load(Data::PhotoSize::Small, parent->fullId());
- }
- }
- Photo::~Photo() = default;
- void Photo::initDimensions() {
- _maxw = 2 * st::overviewPhotoMinSize;
- _minh = _story ? qRound(_maxw * kStoryRatio) : _maxw;
- }
- int32 Photo::resizeGetHeight(int32 width) {
- width = qMin(width, _maxw);
- if (_width != width) {
- _width = width;
- _height = _story ? qRound(_width * kStoryRatio) : _width;
- }
- return _height;
- }
- void Photo::paint(Painter &p, const QRect &clip, TextSelection selection, const PaintContext *context) {
- const auto selected = (selection == FullSelection);
- const auto widthChanged = (_pix.width()
- != (_width * style::DevicePixelRatio()));
- if (!_goodLoaded || widthChanged) {
- ensureDataMediaCreated();
- const auto good = !_spoiler
- && (_dataMedia->loaded()
- || _dataMedia->image(Data::PhotoSize::Thumbnail));
- if ((good && !_goodLoaded) || widthChanged) {
- _goodLoaded = good;
- _pix = QPixmap();
- if (_goodLoaded) {
- setPixFrom(_dataMedia->image(Data::PhotoSize::Large)
- ? _dataMedia->image(Data::PhotoSize::Large)
- : _dataMedia->image(Data::PhotoSize::Thumbnail));
- } else if (const auto small = _spoiler
- ? nullptr
- : _dataMedia->image(Data::PhotoSize::Small)) {
- setPixFrom(small);
- } else if (const auto blurred = _dataMedia->thumbnailInline()) {
- setPixFrom(blurred);
- }
- }
- }
- if (_pix.isNull()) {
- p.fillRect(0, 0, _width, _height, st::overviewPhotoBg);
- } else {
- p.drawPixmap(0, 0, _pix);
- }
- if (_spoiler) {
- const auto paused = context->paused || On(PowerSaving::kChatSpoiler);
- Ui::FillSpoilerRect(
- p,
- QRect(0, 0, _width, _height),
- Ui::DefaultImageSpoiler().frame(
- _spoiler->index(context->ms, paused)));
- }
- if (selected) {
- p.fillRect(0, 0, _width, _height, st::overviewPhotoSelectOverlay);
- }
- if (_pinned) {
- const auto &icon = selected
- ? st::storyPinnedIconSelected
- : st::storyPinnedIcon;
- icon.paint(p, _width - icon.width(), 0, _width);
- }
- const auto checkDelta = st::overviewCheckSkip + st::overviewCheck.size;
- const auto checkLeft = _width - checkDelta;
- const auto checkTop = _height - checkDelta;
- paintCheckbox(p, { checkLeft, checkTop }, selected, context);
- }
- void Photo::setPixFrom(not_null<Image*> image) {
- Expects(_width > 0 && _height > 0);
- auto img = image->original();
- if (!_goodLoaded) {
- img = Images::Blur(std::move(img));
- }
- _pix = Ui::PixmapFromImage(
- CropMediaFrame(std::move(img), _width, _height));
- // In case we have inline thumbnail we can unload all images and we still
- // won't get a blank image in the media viewer when the photo is opened.
- if (!_data->inlineThumbnailBytes().isEmpty()) {
- _dataMedia = nullptr;
- delegate()->unregisterHeavyItem(this);
- }
- }
- void Photo::ensureDataMediaCreated() const {
- if (_dataMedia) {
- return;
- }
- _dataMedia = _data->createMediaView();
- if (_data->inlineThumbnailBytes().isEmpty()) {
- _dataMedia->wanted(Data::PhotoSize::Small, parent()->fullId());
- }
- _dataMedia->wanted(Data::PhotoSize::Thumbnail, parent()->fullId());
- delegate()->registerHeavyItem(this);
- }
- void Photo::clearSpoiler() {
- if (_spoiler) {
- _spoiler = nullptr;
- _pix = QPixmap();
- delegate()->repaintItem(this);
- }
- }
- void Photo::itemDataChanged() {
- const auto pinned = parent()->isPinned();
- if (_pinned != pinned) {
- _pinned = pinned;
- delegate()->repaintItem(this);
- }
- }
- void Photo::clearHeavyPart() {
- _dataMedia = nullptr;
- }
- TextState Photo::getState(
- QPoint point,
- StateRequest request) const {
- if (hasPoint(point)) {
- return { parent(), _link };
- }
- return {};
- }
- Video::Video(
- not_null<Delegate*> delegate,
- not_null<HistoryItem*> parent,
- not_null<DocumentData*> video,
- MediaOptions options)
- : RadialProgressItem(delegate, parent)
- , _data(video)
- , _videoCover(LookupVideoCover(video, parent))
- , _duration(Ui::FormatDurationText(_data->duration() / 1000))
- , _spoiler(options.spoiler ? std::make_unique<Ui::SpoilerAnimation>([=] {
- delegate->repaintItem(this);
- }) : nullptr)
- , _pinned(options.pinned)
- , _story(options.story) {
- setDocumentLinks(_data);
- if (!_videoCover) {
- _data->loadThumbnail(parent->fullId());
- } else if (_videoCover->inlineThumbnailBytes().isEmpty()
- && (_videoCover->hasExact(Data::PhotoSize::Small)
- || _videoCover->hasExact(Data::PhotoSize::Thumbnail))) {
- _videoCover->load(Data::PhotoSize::Small, parent->fullId());
- }
- }
- Video::~Video() = default;
- void Video::initDimensions() {
- _maxw = 2 * st::overviewPhotoMinSize;
- _minh = _story ? qRound(_maxw * kStoryRatio) : _maxw;
- }
- int32 Video::resizeGetHeight(int32 width) {
- width = qMin(width, _maxw);
- if (_width != width) {
- _width = width;
- _height = _story ? qRound(_width * kStoryRatio) : _width;
- }
- return _height;
- }
- void Video::paint(Painter &p, const QRect &clip, TextSelection selection, const PaintContext *context) {
- ensureDataMediaCreated();
- const auto selected = (selection == FullSelection);
- const auto blurred = _videoCover
- ? _videoCoverMedia->thumbnailInline()
- : _dataMedia->thumbnailInline();
- const auto thumbnail = _spoiler
- ? nullptr
- : _videoCover
- ? _videoCoverMedia->image(Data::PhotoSize::Small)
- : _dataMedia->thumbnail();
- const auto good = _spoiler
- ? nullptr
- : _videoCover
- ? _videoCoverMedia->image(Data::PhotoSize::Large)
- : _dataMedia->goodThumbnail();
- bool loaded = dataLoaded(), displayLoading = _data->displayLoading();
- if (displayLoading) {
- ensureRadial();
- if (!_radial->animating()) {
- _radial->start(dataProgress());
- }
- }
- updateStatusText();
- const auto radial = isRadialAnimation();
- const auto radialOpacity = radial ? _radial->opacity() : 0.;
- if ((blurred || thumbnail || good)
- && ((_pix.width() != _width * style::DevicePixelRatio())
- || (_pixBlurred && (thumbnail || good)))) {
- auto img = good
- ? good->original()
- : thumbnail
- ? thumbnail->original()
- : Images::Blur(blurred->original());
- _pix = Ui::PixmapFromImage(
- CropMediaFrame(std::move(img), _width, _height));
- _pixBlurred = !(thumbnail || good);
- }
- if (_pix.isNull()) {
- p.fillRect(0, 0, _width, _height, st::overviewPhotoBg);
- } else {
- p.drawPixmap(0, 0, _pix);
- }
- if (_spoiler) {
- const auto paused = context->paused || On(PowerSaving::kChatSpoiler);
- Ui::FillSpoilerRect(
- p,
- QRect(0, 0, _width, _height),
- Ui::DefaultImageSpoiler().frame(
- _spoiler->index(context->ms, paused)));
- }
- if (selected) {
- p.fillRect(QRect(0, 0, _width, _height), st::overviewPhotoSelectOverlay);
- }
- if (_pinned) {
- const auto &icon = selected
- ? st::storyPinnedIconSelected
- : st::storyPinnedIcon;
- icon.paint(p, _width - icon.width(), 0, _width);
- }
- if (!selected && !context->selecting && radialOpacity < 1.) {
- if (clip.intersects(QRect(0, _height - st::normalFont->height, _width, st::normalFont->height))) {
- const auto download = !loaded && !_dataMedia->canBePlayed(parent());
- const auto &icon = download
- ? (selected ? st::overviewVideoDownloadSelected : st::overviewVideoDownload)
- : (selected ? st::overviewVideoPlaySelected : st::overviewVideoPlay);
- const auto text = download ? _status.text() : _duration;
- const auto margin = st::overviewVideoStatusMargin;
- const auto padding = st::overviewVideoStatusPadding;
- const auto statusX = margin + padding.x(), statusY = _height - margin - padding.y() - st::normalFont->height;
- const auto statusW = icon.width() + padding.x() + st::normalFont->width(text) + 2 * padding.x();
- const auto statusH = st::normalFont->height + 2 * padding.y();
- p.setOpacity(1. - radialOpacity);
- Ui::FillRoundRect(p, statusX - padding.x(), statusY - padding.y(), statusW, statusH, selected ? st::msgDateImgBgSelected : st::msgDateImgBg, selected ? Ui::OverviewVideoSelectedCorners : Ui::OverviewVideoCorners);
- p.setFont(st::normalFont);
- p.setPen(st::msgDateImgFg);
- icon.paint(p, statusX, statusY + (st::normalFont->height - icon.height()) / 2, _width);
- p.drawTextLeft(statusX + icon.width() + padding.x(), statusY, _width, text, statusW - 2 * padding.x());
- }
- }
- QRect inner((_width - st::overviewVideoRadialSize) / 2, (_height - st::overviewVideoRadialSize) / 2, st::overviewVideoRadialSize, st::overviewVideoRadialSize);
- if (radial && clip.intersects(inner)) {
- p.setOpacity(radialOpacity);
- p.setPen(Qt::NoPen);
- if (selected) {
- p.setBrush(st::msgDateImgBgSelected);
- } else {
- auto over = ClickHandler::showAsActive((_data->loading() || _data->uploading()) ? _cancell : (loaded || _dataMedia->canBePlayed(parent())) ? _openl : _savel);
- p.setBrush(anim::brush(st::msgDateImgBg, st::msgDateImgBgOver, _a_iconOver.value(over ? 1. : 0.)));
- }
- {
- PainterHighQualityEnabler hq(p);
- p.drawEllipse(inner);
- }
- const auto icon = [&] {
- return &(selected ? st::historyFileThumbCancelSelected : st::historyFileThumbCancel);
- }();
- icon->paintInCenter(p, inner);
- if (radial) {
- p.setOpacity(1);
- QRect rinner(inner.marginsRemoved(QMargins(st::msgFileRadialLine, st::msgFileRadialLine, st::msgFileRadialLine, st::msgFileRadialLine)));
- _radial->draw(p, rinner, st::msgFileRadialLine, selected ? st::historyFileThumbRadialFgSelected : st::historyFileThumbRadialFg);
- }
- }
- p.setOpacity(1);
- const auto checkDelta = st::overviewCheckSkip + st::overviewCheck.size;
- const auto checkLeft = _width - checkDelta;
- const auto checkTop = _height - checkDelta;
- paintCheckbox(p, { checkLeft, checkTop }, selected, context);
- }
- void Video::ensureDataMediaCreated() const {
- if (_dataMedia && (!_videoCover || _videoCoverMedia)) {
- return;
- }
- _dataMedia = _data->createMediaView();
- if (_videoCover) {
- _videoCoverMedia = _videoCover->createMediaView();
- _videoCover->load(Data::PhotoSize::Large, parent()->fullId());
- } else {
- _dataMedia->goodThumbnailWanted();
- _dataMedia->thumbnailWanted(parent()->fullId());
- }
- delegate()->registerHeavyItem(this);
- }
- void Video::clearSpoiler() {
- if (_spoiler) {
- _spoiler = nullptr;
- _pix = QPixmap();
- delegate()->repaintItem(this);
- }
- }
- void Video::itemDataChanged() {
- const auto pinned = parent()->isPinned();
- if (_pinned != pinned) {
- _pinned = pinned;
- delegate()->repaintItem(this);
- }
- }
- void Video::clearHeavyPart() {
- _dataMedia = nullptr;
- }
- float64 Video::dataProgress() const {
- ensureDataMediaCreated();
- return _dataMedia->progress();
- }
- bool Video::dataFinished() const {
- return !_data->loading();
- }
- bool Video::dataLoaded() const {
- ensureDataMediaCreated();
- return _dataMedia->loaded();
- }
- bool Video::iconAnimated() const {
- return true;
- }
- TextState Video::getState(
- QPoint point,
- StateRequest request) const {
- if (hasPoint(point)) {
- ensureDataMediaCreated();
- const auto link = (_data->loading() || _data->uploading())
- ? _cancell
- : (dataLoaded() || _dataMedia->canBePlayed(parent()))
- ? _openl
- : _savel;
- return { parent(), link };
- }
- return {};
- }
- void Video::updateStatusText() {
- auto statusSize = int64();
- if (_data->status == FileDownloadFailed || _data->status == FileUploadFailed) {
- statusSize = Ui::FileStatusSizeFailed;
- } else if (_data->uploading()) {
- statusSize = _data->uploadingData->offset;
- } else if (dataLoaded()) {
- statusSize = Ui::FileStatusSizeLoaded;
- } else {
- statusSize = Ui::FileStatusSizeReady;
- }
- if (statusSize != _status.size()) {
- auto status = statusSize;
- auto size = _data->size;
- if (statusSize >= 0 && statusSize < 0xFF000000LL) {
- size = status;
- status = Ui::FileStatusSizeReady;
- }
- _status.update(status, size, -1, 0);
- _status.setSize(statusSize);
- }
- }
- Voice::Voice(
- not_null<Delegate*> delegate,
- not_null<HistoryItem*> parent,
- not_null<DocumentData*> voice,
- const style::OverviewFileLayout &st)
- : RadialProgressItem(delegate, parent)
- , _data(voice)
- , _namel(std::make_shared<DocumentOpenClickHandler>(
- _data,
- crl::guard(this, [=](FullMsgId id) {
- delegate->openDocument(_data, id);
- }),
- parent->fullId()))
- , _st(st) {
- AddComponents(Info::Bit());
- setDocumentLinks(_data);
- _data->loadThumbnail(parent->fullId());
- updateName();
- const auto dateText = Ui::Text::Link(
- langDateTime(base::unixtime::parse(parent->date()))); // Link 1.
- _details.setMarkedText(
- st::defaultTextStyle,
- tr::lng_date_and_duration(
- tr::now,
- lt_date,
- dateText,
- lt_duration,
- { .text = Ui::FormatDurationText(_data->duration() / 1000) },
- Ui::Text::WithEntities));
- _details.setLink(1, JumpToMessageClickHandler(parent));
- }
- void Voice::initDimensions() {
- _maxw = _st.maxWidth;
- _minh = _st.songPadding.top() + _st.songThumbSize + _st.songPadding.bottom() + st::lineWidth;
- }
- void Voice::paint(Painter &p, const QRect &clip, TextSelection selection, const PaintContext *context) {
- ensureDataMediaCreated();
- bool selected = (selection == FullSelection);
- bool loaded = dataLoaded(), displayLoading = _data->displayLoading();
- if (displayLoading) {
- ensureRadial();
- if (!_radial->animating()) {
- _radial->start(dataProgress());
- }
- }
- const auto showPause = updateStatusText();
- const auto nameVersion = parent()->fromOriginal()->nameVersion();
- if (_nameVersion < nameVersion) {
- updateName();
- }
- const auto radial = isRadialAnimation();
- const auto nameleft = _st.songPadding.left()
- + _st.songThumbSize
- + _st.songPadding.right();
- const auto nameright = _st.songPadding.left();
- const auto nametop = _st.songNameTop;
- const auto statustop = _st.songStatusTop;
- const auto namewidth = _width - nameleft - nameright;
- const auto inner = style::rtlrect(
- _st.songPadding.left(),
- _st.songPadding.top(),
- _st.songThumbSize,
- _st.songThumbSize,
- _width);
- if (clip.intersects(inner)) {
- if (_data->hasThumbnail()) {
- ensureDataMediaCreated();
- }
- const auto thumbnail = _dataMedia
- ? _dataMedia->thumbnail()
- : nullptr;
- const auto blurred = _dataMedia
- ? _dataMedia->thumbnailInline()
- : nullptr;
- p.setPen(Qt::NoPen);
- if (thumbnail || blurred) {
- const auto options = Images::Option::RoundCircle
- | (blurred ? Images::Option::Blur : Images::Option());
- const auto thumb = (thumbnail ? thumbnail : blurred)->pix(
- inner.size(),
- { .options = options });
- p.drawPixmap(inner.topLeft(), thumb);
- } else if (_data->hasThumbnail()) {
- PainterHighQualityEnabler hq(p);
- p.setBrush(st::imageBg);
- p.drawEllipse(inner);
- }
- const auto &checkLink = (_data->loading() || _data->uploading())
- ? _cancell
- : (_dataMedia->canBePlayed(parent()) || loaded)
- ? _openl
- : _savel;
- if (selected) {
- p.setBrush((thumbnail || blurred) ? st::msgDateImgBgSelected : st::msgFileInBgSelected);
- } else if (_data->hasThumbnail()) {
- auto over = ClickHandler::showAsActive(checkLink);
- p.setBrush(anim::brush(st::msgDateImgBg, st::msgDateImgBgOver, _a_iconOver.value(over ? 1. : 0.)));
- } else {
- auto over = ClickHandler::showAsActive(checkLink);
- p.setBrush(anim::brush(st::msgFileInBg, st::msgFileInBgOver, _a_iconOver.value(over ? 1. : 0.)));
- }
- {
- PainterHighQualityEnabler hq(p);
- p.drawEllipse(inner);
- }
- if (radial) {
- QRect rinner(inner.marginsRemoved(QMargins(st::msgFileRadialLine, st::msgFileRadialLine, st::msgFileRadialLine, st::msgFileRadialLine)));
- auto &bg = selected ? st::historyFileInRadialFgSelected : st::historyFileInRadialFg;
- _radial->draw(p, rinner, st::msgFileRadialLine, bg);
- }
- const auto icon = [&] {
- if (_data->loading() || _data->uploading()) {
- return &(selected ? _st.voiceCancelSelected : _st.voiceCancel);
- } else if (showPause) {
- return &(selected ? _st.voicePauseSelected : _st.voicePause);
- } else if (_dataMedia->canBePlayed(parent())) {
- return &(selected ? _st.voicePlaySelected : _st.voicePlay);
- }
- return &(selected
- ? _st.voiceDownloadSelected
- : _st.voiceDownload);
- }();
- icon->paintInCenter(p, inner);
- }
- if (clip.intersects(style::rtlrect(nameleft, nametop, namewidth, st::semiboldFont->height, _width))) {
- p.setPen(st::historyFileNameInFg);
- _name.drawLeftElided(p, nameleft, nametop, namewidth, _width);
- }
- if (clip.intersects(style::rtlrect(nameleft, statustop, namewidth, st::normalFont->height, _width))) {
- p.setFont(st::normalFont);
- p.setPen(selected ? st::mediaInFgSelected : st::mediaInFg);
- int32 unreadx = nameleft;
- if (_status.size() == Ui::FileStatusSizeLoaded || _status.size() == Ui::FileStatusSizeReady) {
- p.setTextPalette(selected ? st::mediaInPaletteSelected : st::mediaInPalette);
- _details.drawLeftElided(p, nameleft, statustop, namewidth, _width);
- p.restoreTextPalette();
- unreadx += _details.maxWidth();
- } else {
- int32 statusw = st::normalFont->width(_status.text());
- p.drawTextLeft(nameleft, statustop, _width, _status.text(), statusw);
- unreadx += statusw;
- }
- auto captionLeft = unreadx + st::mediaUnreadSkip;
- if (parent()->hasUnreadMediaFlag() && unreadx + st::mediaUnreadSkip + st::mediaUnreadSize <= _width) {
- p.setPen(Qt::NoPen);
- p.setBrush(selected ? st::msgFileInBgSelected : st::msgFileInBg);
- {
- PainterHighQualityEnabler hq(p);
- p.drawEllipse(style::rtlrect(unreadx + st::mediaUnreadSkip, statustop + st::mediaUnreadTop, st::mediaUnreadSize, st::mediaUnreadSize, _width));
- }
- captionLeft += st::mediaUnreadSkip + st::mediaUnreadSize;
- }
- if (!_caption.isEmpty()) {
- p.setPen(st::historyFileNameInFg);
- const auto w = _width - captionLeft - st::defaultScrollArea.width;
- _caption.draw(p, Ui::Text::PaintContext{
- .position = QPoint(captionLeft, statustop),
- .availableWidth = w,
- .spoiler = Ui::Text::DefaultSpoilerCache(),
- .paused = context
- ? context->paused
- : On(PowerSaving::kEmojiChat),
- .pausedEmoji = On(PowerSaving::kEmojiChat),
- .pausedSpoiler = On(PowerSaving::kChatSpoiler),
- .elisionLines = 1,
- });
- }
- }
- const auto checkDelta = _st.songThumbSize
- + st::overviewCheckSkip
- - st::overviewSmallCheck.size;
- const auto checkLeft = _st.songPadding.left() + checkDelta;
- const auto checkTop = _st.songPadding.top() + checkDelta;
- paintCheckbox(p, { checkLeft, checkTop }, selected, context);
- }
- TextState Voice::getState(
- QPoint point,
- StateRequest request) const {
- ensureDataMediaCreated();
- const auto loaded = dataLoaded();
- const auto nameleft = _st.songPadding.left()
- + _st.songThumbSize
- + _st.songPadding.right();
- const auto nameright = _st.songPadding.left();
- const auto nametop = _st.songNameTop;
- const auto statustop = _st.songStatusTop;
- const auto inner = style::rtlrect(
- _st.songPadding.left(),
- _st.songPadding.top(),
- _st.songThumbSize,
- _st.songThumbSize,
- _width);
- if (inner.contains(point)) {
- const auto link = (_data->loading() || _data->uploading())
- ? _cancell
- : (_dataMedia->canBePlayed(parent()) || loaded)
- ? _openl
- : _savel;
- return { parent(), link };
- }
- auto result = TextState(parent());
- const auto statusmaxwidth = _width - nameleft - nameright;
- const auto statusrect = style::rtlrect(
- nameleft,
- statustop,
- statusmaxwidth,
- st::normalFont->height,
- _width);
- if (statusrect.contains(point)) {
- if (_status.size() == Ui::FileStatusSizeLoaded || _status.size() == Ui::FileStatusSizeReady) {
- auto textState = _details.getStateLeft(point - QPoint(nameleft, statustop), _width, _width);
- result.link = textState.link;
- result.cursor = textState.uponSymbol
- ? HistoryView::CursorState::Text
- : HistoryView::CursorState::None;
- }
- }
- const auto namewidth = std::min(
- _width - nameleft - nameright,
- _name.maxWidth());
- const auto namerect = style::rtlrect(
- nameleft,
- nametop,
- namewidth,
- st::normalFont->height,
- _width);
- if (namerect.contains(point) && !result.link && !_data->loading()) {
- return { parent(), _namel };
- }
- return result;
- }
- void Voice::ensureDataMediaCreated() const {
- if (_dataMedia) {
- return;
- }
- _dataMedia = _data->createMediaView();
- delegate()->registerHeavyItem(this);
- }
- void Voice::clearHeavyPart() {
- _dataMedia = nullptr;
- }
- float64 Voice::dataProgress() const {
- ensureDataMediaCreated();
- return _dataMedia->progress();
- }
- bool Voice::dataFinished() const {
- return !_data->loading();
- }
- bool Voice::dataLoaded() const {
- ensureDataMediaCreated();
- return _dataMedia->loaded();
- }
- bool Voice::iconAnimated() const {
- return true;
- }
- const style::RoundCheckbox &Voice::checkboxStyle() const {
- return st::overviewSmallCheck;
- }
- void Voice::updateName() {
- if (const auto forwarded = parent()->Get<HistoryMessageForwarded>()) {
- const auto info = parent()->originalHiddenSenderInfo();
- const auto name = info
- ? tr::lng_forwarded(tr::now, lt_user, info->nameText().toString())
- : parent()->fromOriginal()->isChannel()
- ? tr::lng_forwarded_channel(
- tr::now,
- lt_channel,
- parent()->fromOriginal()->name())
- : tr::lng_forwarded(
- tr::now,
- lt_user,
- parent()->fromOriginal()->name());
- _name.setText(st::semiboldTextStyle, name, Ui::NameTextOptions());
- } else {
- _name.setText(
- st::semiboldTextStyle,
- parent()->from()->name(),
- Ui::NameTextOptions());
- }
- _nameVersion = parent()->fromOriginal()->nameVersion();
- _caption.setMarkedText(
- st::defaultTextStyle,
- parent()->originalText(),
- Ui::DialogTextOptions(),
- Core::TextContext({
- .session = &parent()->history()->session(),
- .repaint = [=] { delegate()->repaintItem(this); },
- }));
- }
- bool Voice::updateStatusText() {
- auto showPause = false;
- auto statusSize = int64();
- auto realDuration = TimeId();
- if (_data->status == FileDownloadFailed || _data->status == FileUploadFailed) {
- statusSize = Ui::FileStatusSizeFailed;
- } else if (dataLoaded()) {
- statusSize = Ui::FileStatusSizeLoaded;
- } else {
- statusSize = Ui::FileStatusSizeReady;
- }
- const auto state = Media::Player::instance()->getState(AudioMsgId::Type::Voice);
- if (state.id == AudioMsgId(_data, parent()->fullId(), state.id.externalPlayId())
- && !Media::Player::IsStoppedOrStopping(state.state)) {
- statusSize = -1 - (state.position / state.frequency);
- realDuration = (state.length / state.frequency);
- showPause = Media::Player::ShowPauseIcon(state.state);
- }
- if (statusSize != _status.size()) {
- _status.update(statusSize, _data->size, _data->duration() / 1000, realDuration);
- }
- return showPause;
- }
- Document::Document(
- not_null<Delegate*> delegate,
- not_null<HistoryItem*> parent,
- DocumentFields fields,
- const style::OverviewFileLayout &st)
- : RadialProgressItem(delegate, parent)
- , _data(fields.document)
- , _msgl(parent->isHistoryEntry()
- ? JumpToMessageClickHandler(parent)
- : nullptr)
- , _namel(std::make_shared<DocumentOpenClickHandler>(
- _data,
- crl::guard(this, [=](FullMsgId id) {
- delegate->openDocument(_data, id);
- }),
- parent->fullId()))
- , _st(st)
- , _generic(::Layout::DocumentGenericPreview::Create(_data))
- , _forceFileLayout(fields.forceFileLayout)
- , _date(langDateTime(base::unixtime::parse(fields.dateOverride
- ? fields.dateOverride
- : parent->date())))
- , _ext(_generic.ext)
- , _datew(st::normalFont->width(_date)) {
- _name.setMarkedText(
- st::defaultTextStyle,
- (!_forceFileLayout
- ? Ui::Text::FormatSongNameFor(_data).textWithEntities()
- : Ui::Text::FormatDownloadsName(_data)),
- _documentNameOptions);
- AddComponents(Info::Bit());
- setDocumentLinks(_data);
- _status.update(
- Ui::FileStatusSizeReady,
- _data->size,
- songLayout() ? (_data->duration() / 1000) : -1,
- 0);
- if (withThumb()) {
- _data->loadThumbnail(parent->fullId());
- auto tw = style::ConvertScale(_data->thumbnailLocation().width());
- auto th = style::ConvertScale(_data->thumbnailLocation().height());
- if (tw > th) {
- _thumbw = (tw * _st.fileThumbSize) / th;
- } else {
- _thumbw = _st.fileThumbSize;
- }
- } else {
- _thumbw = 0;
- }
- _extw = st::overviewFileExtFont->width(_ext);
- if (_extw > _st.fileThumbSize - st::overviewFileExtPadding * 2) {
- _ext = st::overviewFileExtFont->elided(_ext, _st.fileThumbSize - st::overviewFileExtPadding * 2, Qt::ElideMiddle);
- _extw = st::overviewFileExtFont->width(_ext);
- }
- }
- bool Document::downloadInCorner() const {
- return _data->isAudioFile()
- && parent()->allowsForward()
- && _data->canBeStreamed(parent())
- && !_data->inappPlaybackFailed();
- }
- void Document::initDimensions() {
- _maxw = _st.maxWidth;
- if (songLayout()) {
- _minh = _st.songPadding.top() + _st.songThumbSize + _st.songPadding.bottom();
- } else {
- _minh = _st.filePadding.top() + _st.fileThumbSize + _st.filePadding.bottom() + st::lineWidth;
- }
- }
- void Document::paint(Painter &p, const QRect &clip, TextSelection selection, const PaintContext *context) {
- ensureDataMediaCreated();
- const auto selected = (selection == FullSelection);
- const auto cornerDownload = downloadInCorner();
- _dataMedia->automaticLoad(parent()->fullId(), parent());
- const auto loaded = dataLoaded();
- const auto displayLoading = _data->displayLoading();
- if (displayLoading) {
- ensureRadial();
- if (!_radial->animating()) {
- _radial->start(dataProgress());
- }
- }
- const auto showPause = updateStatusText();
- const auto radial = isRadialAnimation();
- int32 nameleft = 0, nametop = 0, nameright = 0, statustop = 0, datetop = -1;
- const auto wthumb = withThumb();
- const auto isSong = songLayout();
- if (isSong) {
- nameleft = _st.songPadding.left() + _st.songThumbSize + _st.songPadding.right();
- nameright = _st.songPadding.left();
- nametop = _st.songNameTop;
- statustop = _st.songStatusTop;
- auto inner = style::rtlrect(_st.songPadding.left(), _st.songPadding.top(), _st.songThumbSize, _st.songThumbSize, _width);
- if (clip.intersects(inner)) {
- const auto isLoading = (!cornerDownload
- && (_data->loading() || _data->uploading()));
- p.setPen(Qt::NoPen);
- using namespace HistoryView;
- const auto coverDrawn = _data->isSongWithCover()
- && DrawThumbnailAsSongCover(
- p,
- st::songCoverOverlayFg,
- _dataMedia,
- inner,
- selected);
- if (!coverDrawn) {
- if (selected) {
- p.setBrush(st::msgFileInBgSelected);
- } else {
- const auto over = ClickHandler::showAsActive(isLoading
- ? _cancell
- : (loaded || _dataMedia->canBePlayed(parent()))
- ? _openl
- : _savel);
- p.setBrush(anim::brush(
- _st.songIconBg,
- _st.songOverBg,
- _a_iconOver.value(over ? 1. : 0.)));
- }
- PainterHighQualityEnabler hq(p);
- p.drawEllipse(inner);
- }
- const auto icon = [&] {
- if (!coverDrawn) {
- if (isLoading) {
- return &(selected
- ? _st.voiceCancelSelected
- : _st.voiceCancel);
- } else if (showPause) {
- return &(selected
- ? _st.voicePauseSelected
- : _st.voicePause);
- } else if (loaded || _dataMedia->canBePlayed(parent())) {
- return &(selected
- ? _st.voicePlaySelected
- : _st.voicePlay);
- }
- return &(selected
- ? _st.voiceDownloadSelected
- : _st.voiceDownload);
- }
- if (isLoading) {
- return &(selected ? _st.songCancelSelected : _st.songCancel);
- } else if (showPause) {
- return &(selected ? _st.songPauseSelected : _st.songPause);
- } else if (loaded || _dataMedia->canBePlayed(parent())) {
- return &(selected ? _st.songPlaySelected : _st.songPlay);
- }
- return &(selected ? _st.songDownloadSelected : _st.songDownload);
- }();
- icon->paintInCenter(p, inner);
- if (radial && !cornerDownload) {
- auto rinner = inner.marginsRemoved(QMargins(st::msgFileRadialLine, st::msgFileRadialLine, st::msgFileRadialLine, st::msgFileRadialLine));
- auto &bg = selected ? st::historyFileInRadialFgSelected : st::historyFileInRadialFg;
- _radial->draw(p, rinner, st::msgFileRadialLine, bg);
- }
- drawCornerDownload(p, selected, context);
- }
- } else {
- nameleft = _st.fileThumbSize + _st.filePadding.right();
- nametop = st::linksBorder + _st.fileNameTop;
- statustop = st::linksBorder + _st.fileStatusTop;
- datetop = st::linksBorder + _st.fileDateTop;
- QRect border(style::rtlrect(nameleft, 0, _width - nameleft, st::linksBorder, _width));
- if (!context->skipBorder && clip.intersects(border)) {
- p.fillRect(clip.intersected(border), st::linksBorderFg);
- }
- QRect rthumb(style::rtlrect(0, st::linksBorder + _st.filePadding.top(), _st.fileThumbSize, _st.fileThumbSize, _width));
- if (clip.intersects(rthumb)) {
- if (wthumb) {
- ensureDataMediaCreated();
- const auto thumbnail = _dataMedia->thumbnail();
- const auto blurred = _dataMedia->thumbnailInline();
- if (thumbnail || blurred) {
- if (_thumb.isNull() || (thumbnail && !_thumbLoaded)) {
- _thumbLoaded = (thumbnail != nullptr);
- const auto options = Images::Option::RoundSmall
- | (_thumbLoaded
- ? Images::Option()
- : Images::Option::Blur);
- const auto image = thumbnail ? thumbnail : blurred;
- _thumb = image->pixNoCache(
- _thumbw * style::DevicePixelRatio(),
- {
- .options = options,
- .outer = QSize(
- _st.fileThumbSize,
- _st.fileThumbSize),
- });
- }
- p.drawPixmap(rthumb.topLeft(), _thumb);
- } else {
- p.setPen(Qt::NoPen);
- p.setBrush(st::overviewFileThumbBg);
- p.drawRoundedRect(
- rthumb,
- st::roundRadiusSmall,
- st::roundRadiusSmall);
- }
- } else {
- p.setPen(Qt::NoPen);
- p.setBrush(_generic.color);
- p.drawRoundedRect(
- rthumb,
- st::roundRadiusSmall,
- st::roundRadiusSmall);
- if (!radial && loaded && !_ext.isEmpty()) {
- p.setFont(st::overviewFileExtFont);
- p.setPen(st::overviewFileExtFg);
- p.drawText(rthumb.left() + (rthumb.width() - _extw) / 2, rthumb.top() + st::overviewFileExtTop + st::overviewFileExtFont->ascent, _ext);
- }
- }
- if (selected) {
- p.setPen(Qt::NoPen);
- p.setBrush(st::defaultTextPalette.selectOverlay);
- p.drawRoundedRect(
- rthumb,
- st::roundRadiusSmall,
- st::roundRadiusSmall);
- }
- if (radial || (!loaded && !_data->loading())) {
- QRect inner(rthumb.x() + (rthumb.width() - _st.songThumbSize) / 2, rthumb.y() + (rthumb.height() - _st.songThumbSize) / 2, _st.songThumbSize, _st.songThumbSize);
- if (clip.intersects(inner)) {
- auto radialOpacity = (radial && loaded && !_data->uploading()) ? _radial->opacity() : 1;
- p.setPen(Qt::NoPen);
- if (selected) {
- p.setBrush(wthumb
- ? st::msgDateImgBgSelected
- : _generic.selected);
- } else {
- auto over = ClickHandler::showAsActive(_data->loading() ? _cancell : _savel);
- p.setBrush(anim::brush(
- wthumb ? st::msgDateImgBg : _generic.dark,
- wthumb ? st::msgDateImgBgOver : _generic.over,
- _a_iconOver.value(over ? 1. : 0.)));
- }
- p.setOpacity(radialOpacity * p.opacity());
- {
- PainterHighQualityEnabler hq(p);
- p.drawEllipse(inner);
- }
- p.setOpacity(radialOpacity);
- auto icon = ([loaded, this, selected] {
- if (loaded || _data->loading()) {
- return &(selected ? st::historyFileThumbCancelSelected : st::historyFileThumbCancel);
- }
- return &(selected ? st::historyFileThumbDownloadSelected : st::historyFileThumbDownload);
- })();
- icon->paintInCenter(p, inner);
- if (radial) {
- p.setOpacity(1);
- QRect rinner(inner.marginsRemoved(QMargins(st::msgFileRadialLine, st::msgFileRadialLine, st::msgFileRadialLine, st::msgFileRadialLine)));
- _radial->draw(p, rinner, st::msgFileRadialLine, selected ? st::historyFileThumbRadialFgSelected : st::historyFileThumbRadialFg);
- }
- }
- }
- }
- }
- const auto availwidth = _width - nameleft - nameright;
- const auto namewidth = std::min(availwidth, _name.maxWidth());
- if (clip.intersects(style::rtlrect(nameleft, nametop, namewidth, st::semiboldFont->height, _width))) {
- p.setPen(st::historyFileNameInFg);
- _name.drawLeftElided(p, nameleft, nametop, namewidth, _width);
- }
- if (clip.intersects(style::rtlrect(nameleft, statustop, availwidth, st::normalFont->height, _width))) {
- p.setFont(st::normalFont);
- p.setPen((isSong && selected) ? st::mediaInFgSelected : st::mediaInFg);
- p.drawTextLeft(nameleft, statustop, _width, _status.text());
- }
- if (datetop >= 0 && clip.intersects(style::rtlrect(nameleft, datetop, _datew, st::normalFont->height, _width))) {
- p.setFont((_msgl && ClickHandler::showAsActive(_msgl))
- ? st::normalFont->underline()
- : st::normalFont);
- p.setPen(st::mediaInFg);
- p.drawTextLeft(nameleft, datetop, _width, _date, _datew);
- }
- const auto checkDelta = (isSong ? _st.songThumbSize : _st.fileThumbSize)
- + (isSong ? st::overviewCheckSkip : -st::overviewCheckSkip)
- - st::overviewSmallCheck.size;
- const auto checkLeft = (isSong
- ? _st.songPadding.left()
- : 0) + checkDelta;
- const auto checkTop = (isSong
- ? _st.songPadding.top()
- : (st::linksBorder + _st.filePadding.top())) + checkDelta;
- paintCheckbox(p, { checkLeft, checkTop }, selected, context);
- }
- void Document::drawCornerDownload(QPainter &p, bool selected, const PaintContext *context) const {
- if (dataLoaded()
- || _data->loadedInMediaCache()
- || !downloadInCorner()) {
- return;
- }
- const auto size = st::overviewSmallCheck.size;
- const auto shift = _st.songThumbSize + st::overviewCheckSkip - size;
- const auto inner = style::rtlrect(_st.songPadding.left() + shift, _st.songPadding.top() + shift, size, size, _width);
- auto pen = st::windowBg->p;
- pen.setWidth(st::lineWidth);
- p.setPen(pen);
- if (selected) {
- p.setBrush(st::msgFileInBgSelected);
- } else {
- p.setBrush(_st.songIconBg);
- }
- {
- PainterHighQualityEnabler hq(p);
- p.drawEllipse(inner);
- }
- const auto icon = [&] {
- if (_data->loading()) {
- return &(selected ? st::overviewSmallCancelSelected : st::overviewSmallCancel);
- }
- return &(selected ? st::overviewSmallDownloadSelected : st::overviewSmallDownload);
- }();
- icon->paintInCenter(p, inner);
- if (_radial && _radial->animating()) {
- const auto rinner = inner.marginsRemoved(QMargins(st::historyAudioRadialLine, st::historyAudioRadialLine, st::historyAudioRadialLine, st::historyAudioRadialLine));
- const auto &fg = selected
- ? st::historyFileInIconFgSelected
- : st::historyFileInIconFg;
- _radial->draw(p, rinner, st::historyAudioRadialLine, fg);
- }
- }
- TextState Document::cornerDownloadTextState(
- QPoint point,
- StateRequest request) const {
- auto result = TextState(parent());
- if (!downloadInCorner()
- || dataLoaded()
- || _data->loadedInMediaCache()) {
- return result;
- }
- const auto size = st::overviewSmallCheck.size;
- const auto shift = _st.songThumbSize + st::overviewCheckSkip - size;
- const auto inner = style::rtlrect(_st.songPadding.left() + shift, _st.songPadding.top() + shift, size, size, _width);
- if (inner.contains(point)) {
- result.link = _data->loading() ? _cancell : _savel;
- }
- return result;
- }
- TextState Document::getState(
- QPoint point,
- StateRequest request) const {
- ensureDataMediaCreated();
- const auto loaded = dataLoaded();
- if (songLayout()) {
- const auto nameleft = _st.songPadding.left() + _st.songThumbSize + _st.songPadding.right();
- const auto nameright = _st.songPadding.left();
- const auto namewidth = std::min(
- _width - nameleft - nameright,
- _name.maxWidth());
- const auto nametop = _st.songNameTop;
- if (const auto state = cornerDownloadTextState(point, request); state.link) {
- return state;
- }
- const auto inner = style::rtlrect(
- _st.songPadding.left(),
- _st.songPadding.top(),
- _st.songThumbSize,
- _st.songThumbSize,
- _width);
- if (inner.contains(point)) {
- const auto link = (!downloadInCorner()
- && (_data->loading() || _data->uploading()))
- ? _cancell
- : (loaded || _dataMedia->canBePlayed(parent()))
- ? _openl
- : _savel;
- return { parent(), link };
- }
- const auto namerect = style::rtlrect(
- nameleft,
- nametop,
- namewidth,
- st::semiboldFont->height,
- _width);
- if (namerect.contains(point) && !_data->loading()) {
- return { parent(), _namel };
- }
- } else {
- const auto nameleft = _st.fileThumbSize + _st.filePadding.right();
- const auto nameright = 0;
- const auto nametop = st::linksBorder + _st.fileNameTop;
- const auto namewidth = std::min(
- _width - nameleft - nameright,
- _name.maxWidth());
- const auto datetop = st::linksBorder + _st.fileDateTop;
- const auto rthumb = style::rtlrect(
- 0,
- st::linksBorder + _st.filePadding.top(),
- _st.fileThumbSize,
- _st.fileThumbSize,
- _width);
- if (rthumb.contains(point)) {
- const auto link = (_data->loading() || _data->uploading())
- ? _cancell
- : loaded
- ? _openl
- : _savel;
- return { parent(), link };
- }
- if (_data->status != FileUploadFailed) {
- auto daterect = style::rtlrect(
- nameleft,
- datetop,
- _datew,
- st::normalFont->height,
- _width);
- if (daterect.contains(point)) {
- return { parent(), _msgl };
- }
- }
- if (!_data->loading() && !_data->isNull()) {
- auto leftofnamerect = style::rtlrect(
- 0,
- st::linksBorder,
- nameleft,
- _height - st::linksBorder,
- _width);
- if (loaded && leftofnamerect.contains(point)) {
- return { parent(), _namel };
- }
- const auto namerect = style::rtlrect(
- nameleft,
- nametop,
- namewidth,
- st::semiboldFont->height,
- _width);
- if (namerect.contains(point)) {
- return { parent(), _namel };
- }
- }
- }
- return {};
- }
- const style::RoundCheckbox &Document::checkboxStyle() const {
- return st::overviewSmallCheck;
- }
- bool Document::songLayout() const {
- return !_forceFileLayout && _data->isSong();
- }
- void Document::ensureDataMediaCreated() const {
- if (_dataMedia) {
- return;
- }
- _dataMedia = _data->createMediaView();
- _dataMedia->thumbnailWanted(parent()->fullId());
- delegate()->registerHeavyItem(this);
- }
- void Document::clearHeavyPart() {
- _dataMedia = nullptr;
- }
- float64 Document::dataProgress() const {
- ensureDataMediaCreated();
- return _dataMedia->progress();
- }
- bool Document::dataFinished() const {
- return !_data->loading();
- }
- bool Document::dataLoaded() const {
- ensureDataMediaCreated();
- return _dataMedia->loaded();
- }
- bool Document::iconAnimated() const {
- return songLayout()
- || !dataLoaded()
- || (_radial && _radial->animating());
- }
- bool Document::withThumb() const {
- return !songLayout() && _data->hasThumbnail();
- }
- bool Document::updateStatusText() {
- auto showPause = false;
- auto statusSize = int64();
- auto realDuration = TimeId();
- if (_data->status == FileDownloadFailed
- || _data->status == FileUploadFailed) {
- statusSize = Ui::FileStatusSizeFailed;
- } else if (_data->uploading()) {
- statusSize = _data->uploadingData->offset;
- } else if (_data->loading()) {
- statusSize = _data->loadOffset();
- } else if (dataLoaded()) {
- statusSize = Ui::FileStatusSizeLoaded;
- } else {
- statusSize = Ui::FileStatusSizeReady;
- }
- const auto isSong = songLayout();
- if (isSong) {
- const auto state = Media::Player::instance()->getState(AudioMsgId::Type::Song);
- if (state.id == AudioMsgId(_data, parent()->fullId(), state.id.externalPlayId()) && !Media::Player::IsStoppedOrStopping(state.state)) {
- statusSize = -1 - (state.position / state.frequency);
- realDuration = (state.length / state.frequency);
- showPause = Media::Player::ShowPauseIcon(state.state);
- }
- if (!showPause && (state.id == AudioMsgId(_data, parent()->fullId(), state.id.externalPlayId())) && Media::Player::instance()->isSeeking(AudioMsgId::Type::Song)) {
- showPause = true;
- }
- }
- if (statusSize != _status.size()) {
- _status.update(
- statusSize,
- _data->size,
- isSong ? (_data->duration() / 1000) : -1,
- realDuration);
- }
- return showPause;
- }
- Link::Link(
- not_null<Delegate*> delegate,
- not_null<HistoryItem*> parent,
- Data::Media *media)
- : ItemBase(delegate, parent)
- , _text(st::msgMinWidth) {
- AddComponents(Info::Bit());
- auto textWithEntities = parent->originalText();
- QString mainUrl;
- auto text = textWithEntities.text;
- const auto &entities = textWithEntities.entities;
- int32 from = 0, till = text.size(), lnk = entities.size();
- for (const auto &entity : entities) {
- auto type = entity.type();
- if (type != EntityType::Url && type != EntityType::CustomUrl && type != EntityType::Email) {
- continue;
- }
- const auto customUrl = entity.data();
- const auto entityText = text.mid(entity.offset(), entity.length());
- const auto url = customUrl.isEmpty() ? entityText : customUrl;
- if (_links.isEmpty()) {
- mainUrl = url;
- }
- _links.push_back(LinkEntry(url, entityText));
- }
- if (_links.empty()) {
- if (const auto media = parent->media()) {
- if (const auto webpage = media->webpage()) {
- if (!webpage->displayUrl.isEmpty()
- && !webpage->url.isEmpty()) {
- _links.push_back(
- LinkEntry(webpage->displayUrl, webpage->url));
- }
- }
- }
- }
- while (lnk > 0 && till > from) {
- --lnk;
- auto &entity = entities.at(lnk);
- auto type = entity.type();
- if (type != EntityType::Url && type != EntityType::CustomUrl && type != EntityType::Email) {
- ++lnk;
- break;
- }
- int32 afterLinkStart = entity.offset() + entity.length();
- if (till > afterLinkStart) {
- if (!QRegularExpression(u"^[,.\\s_=+\\-;:`'\"\\(\\)\\[\\]\\{\\}<>*&^%\\$#@!\\\\/]+$"_q).match(text.mid(afterLinkStart, till - afterLinkStart)).hasMatch()) {
- ++lnk;
- break;
- }
- }
- till = entity.offset();
- }
- if (!lnk) {
- if (QRegularExpression(u"^[,.\\s\\-;:`'\"\\(\\)\\[\\]\\{\\}<>*&^%\\$#@!\\\\/]+$"_q).match(text.mid(from, till - from)).hasMatch()) {
- till = from;
- }
- }
- const auto createHandler = [](const QString &url) {
- return UrlClickHandler::IsSuspicious(url)
- ? std::make_shared<HiddenUrlClickHandler>(url)
- : std::make_shared<UrlClickHandler>(url, false);
- };
- _page = media ? media->webpage() : nullptr;
- if (_page) {
- mainUrl = _page->url;
- if (_page->document) {
- _photol = std::make_shared<DocumentOpenClickHandler>(
- _page->document,
- crl::guard(this, [=](FullMsgId id) {
- delegate->openDocument(_page->document, id);
- }),
- parent->fullId());
- } else if (_page->photo) {
- if (_page->type == WebPageType::Profile
- || _page->type == WebPageType::Video) {
- _photol = createHandler(_page->url);
- } else if (_page->type == WebPageType::Photo
- || _page->type == WebPageType::Document
- || _page->siteName == u"Twitter"_q
- || _page->siteName == u"Facebook"_q) {
- _photol = std::make_shared<PhotoOpenClickHandler>(
- _page->photo,
- crl::guard(this, [=](FullMsgId id) {
- delegate->openPhoto(_page->photo, id);
- }),
- parent->fullId());
- } else {
- _photol = createHandler(_page->url);
- }
- } else {
- _photol = createHandler(_page->url);
- }
- } else if (!mainUrl.isEmpty()) {
- _photol = createHandler(mainUrl);
- }
- if (from >= till && _page) {
- text = _page->description.text;
- from = 0;
- till = text.size();
- }
- if (till > from) {
- TextParseOptions opts = { TextParseMultiline, int32(st::linksMaxWidth), 3 * st::normalFont->height, Qt::LayoutDirectionAuto };
- _text.setText(st::defaultTextStyle, text.mid(from, till - from), opts);
- }
- int32 tw = 0, th = 0;
- if (_page && _page->photo) {
- const auto photo = _page->photo;
- if (photo->hasExact(Data::PhotoSize::Small)
- || photo->hasExact(Data::PhotoSize::Thumbnail)) {
- photo->load(Data::PhotoSize::Small, parent->fullId());
- }
- tw = style::ConvertScale(photo->width());
- th = style::ConvertScale(photo->height());
- } else if (_page && _page->document && _page->document->hasThumbnail()) {
- _page->document->loadThumbnail(parent->fullId());
- const auto &location = _page->document->thumbnailLocation();
- tw = style::ConvertScale(location.width());
- th = style::ConvertScale(location.height());
- }
- if (tw > st::linksPhotoSize) {
- if (th > tw) {
- th = th * st::linksPhotoSize / tw;
- tw = st::linksPhotoSize;
- } else if (th > st::linksPhotoSize) {
- tw = tw * st::linksPhotoSize / th;
- th = st::linksPhotoSize;
- }
- }
- _pixw = qMax(tw, 1);
- _pixh = qMax(th, 1);
- if (_page) {
- _title = _page->title;
- }
- auto parts = QStringView(mainUrl).split('/');
- if (!parts.isEmpty()) {
- auto domain = parts.at(0);
- if (parts.size() > 2 && domain.endsWith(':') && parts.at(1).isEmpty()) { // http:// and others
- domain = parts.at(2);
- }
- parts = domain.split('@').constLast().split('.', Qt::SkipEmptyParts);
- if (parts.size() > 1) {
- _letter = parts.at(parts.size() - 2).at(0).toUpper();
- if (_title.isEmpty()) {
- _title.reserve(parts.at(parts.size() - 2).size());
- _title.append(_letter).append(parts.at(parts.size() - 2).mid(1));
- }
- }
- }
- _titlew = st::semiboldFont->width(_title);
- }
- void Link::initDimensions() {
- _maxw = st::linksMaxWidth;
- _minh = 0;
- if (!_title.isEmpty()) {
- _minh += st::semiboldFont->height;
- }
- if (!_text.isEmpty()) {
- _minh += qMin(3 * st::normalFont->height, _text.countHeight(_maxw - st::linksPhotoSize - st::linksPhotoPadding));
- }
- _minh += _links.size() * st::normalFont->height;
- _minh = qMax(_minh, int32(st::linksPhotoSize)) + st::linksMargin.top() + st::linksMargin.bottom() + st::linksBorder;
- }
- int32 Link::resizeGetHeight(int32 width) {
- _width = qMin(width, _maxw);
- int32 w = _width - st::linksPhotoSize - st::linksPhotoPadding;
- for (const auto &link : std::as_const(_links)) {
- link.lnk->setFullDisplayed(w >= link.width);
- }
- _height = 0;
- if (!_title.isEmpty()) {
- _height += st::semiboldFont->height;
- }
- if (!_text.isEmpty()) {
- _height += qMin(3 * st::normalFont->height, _text.countHeight(_width - st::linksPhotoSize - st::linksPhotoPadding));
- }
- _height += _links.size() * st::normalFont->height;
- _height = qMax(_height, int32(st::linksPhotoSize)) + st::linksMargin.top() + st::linksMargin.bottom() + st::linksBorder;
- return _height;
- }
- void Link::paint(Painter &p, const QRect &clip, TextSelection selection, const PaintContext *context) {
- auto selected = (selection == FullSelection);
- const auto pixLeft = 0;
- const auto pixTop = st::linksMargin.top() + st::linksBorder;
- if (clip.intersects(style::rtlrect(0, pixTop, st::linksPhotoSize, st::linksPhotoSize, _width))) {
- validateThumbnail();
- if (!_thumbnail.isNull()) {
- p.drawPixmap(pixLeft, pixTop, _thumbnail);
- }
- }
- const auto left = st::linksPhotoSize + st::linksPhotoPadding;
- const auto w = _width - left;
- auto top = [&] {
- if (!_title.isEmpty() && _text.isEmpty() && _links.size() == 1) {
- return pixTop + (st::linksPhotoSize - st::semiboldFont->height - st::normalFont->height) / 2;
- }
- return st::linksTextTop;
- }();
- p.setPen(st::linksTextFg);
- p.setFont(st::semiboldFont);
- if (!_title.isEmpty()) {
- if (clip.intersects(style::rtlrect(left, top, qMin(w, _titlew), st::semiboldFont->height, _width))) {
- p.drawTextLeft(left, top, _width, (w < _titlew) ? st::semiboldFont->elided(_title, w) : _title);
- }
- top += st::semiboldFont->height;
- }
- p.setFont(st::msgFont);
- if (!_text.isEmpty()) {
- int32 h = qMin(st::normalFont->height * 3, _text.countHeight(w));
- if (clip.intersects(style::rtlrect(left, top, w, h, _width))) {
- _text.drawLeftElided(p, left, top, w, _width, 3);
- }
- top += h;
- }
- p.setPen(st::windowActiveTextFg);
- for (const auto &link : std::as_const(_links)) {
- if (clip.intersects(style::rtlrect(left, top, qMin(w, link.width), st::normalFont->height, _width))) {
- p.setFont(ClickHandler::showAsActive(link.lnk) ? st::normalFont->underline() : st::normalFont);
- p.drawTextLeft(left, top, _width, (w < link.width) ? st::normalFont->elided(link.text, w) : link.text);
- }
- top += st::normalFont->height;
- }
- QRect border(style::rtlrect(left, 0, w, st::linksBorder, _width));
- if (!context->skipBorder && clip.intersects(border)) {
- p.fillRect(clip.intersected(border), st::linksBorderFg);
- }
- const auto checkDelta = st::linksPhotoSize + st::overviewCheckSkip
- - st::overviewSmallCheck.size;
- const auto checkLeft = pixLeft + checkDelta;
- const auto checkTop = pixTop + checkDelta;
- paintCheckbox(p, { checkLeft, checkTop }, selected, context);
- }
- void Link::validateThumbnail() {
- if (!_thumbnail.isNull() && !_thumbnailBlurred) {
- return;
- }
- const auto size = QSize(_pixw, _pixh);
- const auto outer = QSize(st::linksPhotoSize, st::linksPhotoSize);
- if (_page && _page->photo) {
- using Data::PhotoSize;
- ensurePhotoMediaCreated();
- const auto args = Images::PrepareArgs{
- .options = Images::Option::RoundSmall,
- .outer = outer,
- };
- if (const auto thumbnail = _photoMedia->image(PhotoSize::Thumbnail)) {
- _thumbnail = thumbnail->pixSingle(size, args);
- _thumbnailBlurred = false;
- } else if (const auto large = _photoMedia->image(PhotoSize::Large)) {
- _thumbnail = large->pixSingle(size, args);
- _thumbnailBlurred = false;
- } else if (const auto small = _photoMedia->image(PhotoSize::Small)) {
- _thumbnail = small->pixSingle(size, args);
- _thumbnailBlurred = false;
- } else if (const auto blurred = _photoMedia->thumbnailInline()) {
- _thumbnail = blurred->pixSingle(size, args.blurred());
- return;
- } else {
- return;
- }
- _photoMedia = nullptr;
- delegate()->unregisterHeavyItem(this);
- } else if (_page && _page->document && _page->document->hasThumbnail()) {
- ensureDocumentMediaCreated();
- const auto args = Images::PrepareArgs{
- .options = (_page->document->isVideoMessage()
- ? Images::Option::RoundCircle
- : Images::Option::RoundSmall),
- .outer = outer,
- };
- if (const auto thumbnail = _documentMedia->thumbnail()) {
- _thumbnail = thumbnail->pixSingle(size, args);
- _thumbnailBlurred = false;
- } else if (const auto blurred = _documentMedia->thumbnailInline()) {
- _thumbnail = blurred->pixSingle(size, args.blurred());
- return;
- } else {
- return;
- }
- _documentMedia = nullptr;
- delegate()->unregisterHeavyItem(this);
- } else {
- const auto size = QSize(st::linksPhotoSize, st::linksPhotoSize);
- _thumbnail = QPixmap(size * style::DevicePixelRatio());
- _thumbnail.fill(Qt::transparent);
- auto p = Painter(&_thumbnail);
- const auto index = _letter.isEmpty()
- ? 0
- : (_letter[0].unicode() % 4);
- const auto fill = [&](style::color color, Ui::CachedRoundCorners corners) {
- auto pixRect = QRect(
- 0,
- 0,
- st::linksPhotoSize,
- st::linksPhotoSize);
- Ui::FillRoundRect(p, pixRect, color, corners);
- };
- switch (index) {
- case 0: fill(st::msgFile1Bg, Ui::Doc1Corners); break;
- case 1: fill(st::msgFile2Bg, Ui::Doc2Corners); break;
- case 2: fill(st::msgFile3Bg, Ui::Doc3Corners); break;
- case 3: fill(st::msgFile4Bg, Ui::Doc4Corners); break;
- }
- if (!_letter.isEmpty()) {
- p.setFont(st::linksLetterFont);
- p.setPen(st::linksLetterFg);
- p.drawText(
- QRect(0, 0, st::linksPhotoSize, st::linksPhotoSize),
- _letter,
- style::al_center);
- }
- _thumbnailBlurred = false;
- }
- }
- void Link::ensurePhotoMediaCreated() {
- if (_photoMedia) {
- return;
- }
- _photoMedia = _page->photo->createMediaView();
- _photoMedia->wanted(Data::PhotoSize::Small, parent()->fullId());
- delegate()->registerHeavyItem(this);
- }
- void Link::ensureDocumentMediaCreated() {
- if (_documentMedia) {
- return;
- }
- _documentMedia = _page->document->createMediaView();
- _documentMedia->thumbnailWanted(parent()->fullId());
- delegate()->registerHeavyItem(this);
- }
- void Link::clearHeavyPart() {
- _photoMedia = nullptr;
- _documentMedia = nullptr;
- }
- TextState Link::getState(
- QPoint point,
- StateRequest request) const {
- int32 left = st::linksPhotoSize + st::linksPhotoPadding, top = st::linksMargin.top() + st::linksBorder, w = _width - left;
- if (style::rtlrect(0, top, st::linksPhotoSize, st::linksPhotoSize, _width).contains(point)) {
- return { parent(), _photol };
- }
- if (!_title.isEmpty() && _text.isEmpty() && _links.size() == 1) {
- top += (st::linksPhotoSize - st::semiboldFont->height - st::normalFont->height) / 2;
- }
- if (!_title.isEmpty()) {
- if (style::rtlrect(left, top, qMin(w, _titlew), st::semiboldFont->height, _width).contains(point)) {
- return { parent(), _photol };
- }
- top += st::webPageTitleFont->height;
- }
- if (!_text.isEmpty()) {
- top += qMin(st::normalFont->height * 3, _text.countHeight(w));
- }
- for (const auto &link : _links) {
- if (style::rtlrect(left, top, qMin(w, link.width), st::normalFont->height, _width).contains(point)) {
- return { parent(), ClickHandlerPtr(link.lnk) };
- }
- top += st::normalFont->height;
- }
- return {};
- }
- const style::RoundCheckbox &Link::checkboxStyle() const {
- return st::overviewSmallCheck;
- }
- Link::LinkEntry::LinkEntry(const QString &url, const QString &text)
- : text(text)
- , width(st::normalFont->width(text))
- , lnk(UrlClickHandler::IsSuspicious(url)
- ? std::make_shared<HiddenUrlClickHandler>(url)
- : std::make_shared<UrlClickHandler>(url)) {
- }
- // Copied from inline_bot_layout_internal.
- Gif::Gif(
- not_null<Delegate*> delegate,
- not_null<HistoryItem*> parent,
- not_null<DocumentData*> gif)
- : RadialProgressItem(delegate, parent)
- , _data(gif) {
- setDocumentLinks(_data, true);
- _data->loadThumbnail(parent->fullId());
- }
- Gif::~Gif() = default;
- int Gif::contentWidth() const {
- if (_data->dimensions.width() > 0) {
- return _data->dimensions.width();
- }
- return style::ConvertScale(_data->thumbnailLocation().width());
- }
- int Gif::contentHeight() const {
- if (_data->dimensions.height() > 0) {
- return _data->dimensions.height();
- }
- return style::ConvertScale(_data->thumbnailLocation().height());
- }
- void Gif::initDimensions() {
- int32 w = contentWidth(), h = contentHeight();
- if (w <= 0 || h <= 0) {
- _maxw = 0;
- } else {
- w = w * st::inlineMediaHeight / h;
- _maxw = qMax(w, int32(st::inlineResultsMinWidth));
- }
- _minh = st::inlineMediaHeight + st::inlineResultsSkip;
- }
- int32 Gif::resizeGetHeight(int32 width) {
- _width = width;
- _height = _minh;
- return _height;
- }
- QSize Gif::countFrameSize() const {
- const auto animating = (_gif && _gif->ready());
- auto framew = animating ? _gif->width() : contentWidth();
- auto frameh = animating ? _gif->height() : contentHeight();
- const auto height = st::inlineMediaHeight;
- const auto maxSize = st::maxStickerSize;
- if (framew * height > frameh * _width) {
- if (framew < maxSize || frameh > height) {
- if (frameh > height || (framew * height / frameh) <= maxSize) {
- framew = framew * height / frameh;
- frameh = height;
- } else {
- frameh = int32(frameh * maxSize) / framew;
- framew = maxSize;
- }
- }
- } else {
- if (frameh < maxSize || framew > _width) {
- if (framew > _width || (frameh * _width / framew) <= maxSize) {
- frameh = frameh * _width / framew;
- framew = _width;
- } else {
- framew = int32(framew * maxSize) / frameh;
- frameh = maxSize;
- }
- }
- }
- return QSize(framew, frameh);
- }
- void Gif::clipCallback(Media::Clip::Notification notification) {
- using namespace Media::Clip;
- switch (notification) {
- case Notification::Reinit: {
- if (_gif) {
- if (_gif->state() == State::Error) {
- _gif.setBad();
- } else if (_gif->ready() && !_gif->started()) {
- if (_gif->width() * _gif->height() > kMaxInlineArea) {
- _data->dimensions = QSize(
- _gif->width(),
- _gif->height());
- _gif.reset();
- } else {
- _gif->start({
- .frame = countFrameSize(),
- .outer = { _width, st::inlineMediaHeight },
- });
- }
- } else if (_gif->autoPausedGif()
- && !delegate()->itemVisible(this)) {
- clearHeavyPart();
- }
- }
- update();
- } break;
- case Notification::Repaint: {
- if (_gif && !_gif->currentDisplayed()) {
- update();
- }
- } break;
- }
- }
- void Gif::validateThumbnail(
- Image *image,
- QSize size,
- QSize frame,
- bool good) {
- if (!image || (_thumbGood && !good)) {
- return;
- } else if ((_thumb.size() == size * style::DevicePixelRatio())
- && (_thumbGood || !good)) {
- return;
- }
- _thumbGood = good;
- _thumb = image->pixNoCache(
- frame * style::DevicePixelRatio(),
- {
- .options = (good ? Images::Option() : Images::Option::Blur),
- .outer = size,
- }).toImage();
- }
- void Gif::prepareThumbnail(QSize size, QSize frame) {
- const auto document = _data;
- Assert(document != nullptr);
- ensureDataMediaCreated();
- validateThumbnail(_dataMedia->thumbnail(), size, frame, true);
- validateThumbnail(_dataMedia->thumbnailInline(), size, frame, false);
- }
- void Gif::paint(
- Painter &p,
- const QRect &clip,
- TextSelection selection,
- const PaintContext *context) {
- const auto document = _data;
- ensureDataMediaCreated();
- const auto preview = Data::VideoPreviewState(_dataMedia.get());
- preview.automaticLoad(getItem()->fullId());
- const auto displayLoading = !preview.usingThumbnail()
- && document->displayLoading();
- const auto loaded = preview.loaded();
- const auto loading = preview.loading();
- if (loaded
- && !_gif
- && !_gif.isBad()
- && CanPlayInline(document)) {
- auto that = const_cast<Gif*>(this);
- that->_gif = preview.makeAnimation([=](
- Media::Clip::Notification notification) {
- that->clipCallback(notification);
- });
- }
- const auto animating = (_gif && _gif->started());
- if (displayLoading) {
- ensureRadial();
- if (!_radial->animating()) {
- _radial->start(dataProgress());
- }
- }
- const auto radial = isRadialAnimation();
- const auto frame = countFrameSize();
- const auto r = QRect(0, 0, _width, st::inlineMediaHeight);
- if (animating) {
- const auto pixmap = _gif->current({
- .frame = frame,
- .outer = r.size(),
- }, context->paused ? 0 : context->ms);
- if (_thumb.isNull()) {
- _thumb = pixmap;
- _thumbGood = true;
- }
- p.drawImage(r.topLeft(), pixmap);
- } else {
- prepareThumbnail(r.size(), frame);
- if (_thumb.isNull()) {
- p.fillRect(r, st::overviewPhotoBg);
- } else {
- p.drawImage(r.topLeft(), _thumb);
- }
- }
- const auto selected = (selection == FullSelection);
- if (radial
- || _gif.isBad()
- || (!_gif && !loaded && !loading && !preview.usingThumbnail())) {
- const auto radialOpacity = (radial && loaded)
- ? _radial->opacity()
- : 1.;
- p.fillRect(r, st::msgDateImgBg);
- p.setOpacity(radialOpacity);
- auto icon = [&] {
- if (radial || loading) {
- return &st::historyFileInCancel;
- } else if (loaded) {
- return &st::historyFileInPlay;
- }
- return &st::historyFileInDownload;
- }();
- const auto size = st::overviewVideoRadialSize;
- QRect inner(
- (r.width() - size) / 2,
- (r.height() - size) / 2,
- size,
- size);
- icon->paintInCenter(p, inner);
- if (radial) {
- p.setOpacity(1);
- const auto margin = st::msgFileRadialLine;
- const auto rinner = inner
- - QMargins(margin, margin, margin, margin);
- auto &bg = selected
- ? st::historyFileInRadialFgSelected
- : st::historyFileInRadialFg;
- _radial->draw(p, rinner, st::msgFileRadialLine, bg);
- }
- }
- const auto checkDelta = st::overviewCheckSkip + st::overviewCheck.size;
- const auto checkLeft = _width - checkDelta;
- const auto checkTop = st::overviewCheckSkip;
- paintCheckbox(p, { checkLeft, checkTop }, selected, context);
- }
- void Gif::update() {
- delegate()->repaintItem(this);
- }
- void Gif::ensureDataMediaCreated() const {
- if (_dataMedia) {
- return;
- }
- _dataMedia = _data->createMediaView();
- _dataMedia->goodThumbnailWanted();
- _dataMedia->thumbnailWanted(parent()->fullId());
- delegate()->registerHeavyItem(this);
- }
- void Gif::clearHeavyPart() {
- _gif.reset();
- _dataMedia = nullptr;
- }
- void Gif::setPosition(int32 position) {
- AbstractLayoutItem::setPosition(position);
- if (position < 0) {
- _gif.reset();
- }
- }
- float64 Gif::dataProgress() const {
- ensureDataMediaCreated();
- return _dataMedia->progress();
- }
- bool Gif::dataFinished() const {
- return !_data->loading();
- }
- bool Gif::dataLoaded() const {
- ensureDataMediaCreated();
- const auto preview = Data::VideoPreviewState(_dataMedia.get());
- return preview.loaded();
- }
- bool Gif::iconAnimated() const {
- return true;
- }
- TextState Gif::getState(
- QPoint point,
- StateRequest request) const {
- if (hasPoint(point)) {
- const auto link = (_data->loading() || _data->uploading())
- ? _cancell
- : dataLoaded()
- ? _openl
- : _savel;
- return { parent(), link };
- }
- return {};
- }
- void Gif::updateStatusText() {
- auto statusSize = int64();
- if (_data->status == FileDownloadFailed || _data->status == FileUploadFailed) {
- statusSize = Ui::FileStatusSizeFailed;
- } else if (_data->uploading()) {
- statusSize = _data->uploadingData->offset;
- } else if (dataLoaded()) {
- statusSize = Ui::FileStatusSizeLoaded;
- } else {
- statusSize = Ui::FileStatusSizeReady;
- }
- if (statusSize != _status.size()) {
- auto status = statusSize;
- auto size = _data->size;
- if (statusSize >= 0 && statusSize < 0xFF000000LL) {
- size = status;
- status = Ui::FileStatusSizeReady;
- }
- _status.update(status, size, -1, 0);
- _status.setSize(statusSize);
- }
- }
- } // namespace Layout
- } // namespace Overview
|