diff --git a/Telegram/Resources/style.txt b/Telegram/Resources/style.txt index 5627a3c56..d82fd5e5a 100644 --- a/Telegram/Resources/style.txt +++ b/Telegram/Resources/style.txt @@ -774,6 +774,8 @@ historyToEndSkip: 10px; activeFadeInDuration: 500; activeFadeOutDuration: 3000; +msgRadius: 3px; + msgMaxWidth: 550px; msgFont: font(fsize); msgNameFont: font(fsize semibold); @@ -788,12 +790,11 @@ msgPadding: margins(13px, 7px, 13px, 8px); msgMargin: margins(13px, 4px, 53px, 4px); msgLnkPadding: 2px; // for media open / save links msgBorder: #f0f0f0; -msgOutBG: #effdde; -msgInBG: #fff; -msgOutSelectBG: #b7dbdb; -msgInSelectBG: #c2dcf2; // #358cd4 with 30% opacity -msgOutSelectOverlay: #358cd44c; -msgInSelectOverlay: #358cd44c; +msgOutBg: #effdde; +msgInBg: #fff; +msgOutSelectBg: #b7dbdb; +msgInSelectBg: #c2dcf2; // #358cd4 with 30% opacity +msgSelectOverlay: #358cd44c; msgStickerOverlay: #358cd47f; msgOutServiceColor: #3a8e26; msgInServiceColor: #0e7acd; @@ -818,10 +819,8 @@ msgInReplyBarColor: #2fa9e2; msgOutReplyBarSelColor: #4da79f; msgInReplyBarSelColor: #2fa9e2; -msgServiceSelectBG: #fff4; -msgServiceRadius: 2px; - -msgServiceBG: #89a0b47f; +msgServiceBg: #89a0b47f; +msgServiceSelectBg: #bbc8d4a2; msgServiceColor: #FFF; msgServicePadding: margins(12px, 3px, 12px, 4px); msgServiceMargin: margins(10px, 7px, 80px, 7px); @@ -851,6 +850,7 @@ msgImgDblCheckRect: sprite(300px, 65px, 20px, 20px); msgDateImgDelta: 4px; msgDateImgColor: #fff; msgDateImgBg: #00000054; +msgDateImgSelectBg: #1c4a7187; msgDateImgPadding: point(8px, 2px); msgDateImgCheckSpace: 4px; @@ -862,23 +862,23 @@ defaultTextStyle: textStyle { lnkOverFlags: font(fsize underline); lnkColor: btnYesColor; lnkDownColor: btnYesHover; - selectBG: msgInSelectBG; - selectOverlay: msgInSelectOverlay; + selectBg: msgInSelectBg; + selectOverlay: msgSelectOverlay; lineHeight: 0px; } serviceTextStyle: textStyle(defaultTextStyle) { lnkColor: msgServiceColor; lnkDownColor: msgServiceColor; - selectBG: msgServiceSelectBG; - selectOverlay: msgServiceSelectBG; + selectBg: msgServiceSelectBg; + selectOverlay: msgServiceSelectBg; } inTextStyle: textStyle(defaultTextStyle) { - selectBG: msgInSelectBG; - selectOverlay: msgInSelectOverlay; + selectBg: msgInSelectBg; + selectOverlay: msgSelectOverlay; } outTextStyle: textStyle(defaultTextStyle) { - selectBG: msgOutSelectBG; - selectOverlay: msgOutSelectOverlay; + selectBg: msgOutSelectBg; + selectOverlay: msgSelectOverlay; } medviewSaveAsTextStyle: textStyle(defaultTextStyle) { lnkColor: #91d9ff; @@ -1177,10 +1177,9 @@ btnShareContact: flatButton(btnDefNext, btnDefBig) { } forwardWidth: 364px; -forwardRadius: 2px; forwardMargins: margins(30px, 10px, 30px, 10px); forwardFont: font(16px); -forwardBG: rgba(0, 0, 0, 76); +forwardBg: rgba(0, 0, 0, 76); btnProfileCancel: flatButton(btnDefFlat, btnDefBig) { color: #666d78; overColor: #666d78; @@ -1618,7 +1617,6 @@ emojiPanSize: size(39px, 35px); emojiPanFullSize: size(300px, 321px); emojiPanDuration: 200; emojiPanHover: #f0f4f7; -emojiPanRound: 2px; emojiPanHeader: 42px; emojiPanHeaderFont: font(fsize semibold); @@ -1638,7 +1636,6 @@ emojiSwitchEmoji: sprite(310px, 328px, 8px, 12px); emojiSwitchColor: #42a8db; stickerPanSize: size(55px, 55px); -stickerPanRound: 3px; stickerPanPadding: 11px; stickerPanDelete: sprite(123px, 132px, 12px, 12px); stickerPanDeleteOpacity: 0.5; @@ -1730,11 +1727,16 @@ mvDocLink: linkButton(btnDefLink) { mvDeltaFromLastAction: 5px; mvSwipeDistance: 80px; +mvCaptionPadding: margins(18px, 10px, 18px, 10px); +mvCaptionMargin: size(11px, 11px); +mvCaptionRadius: 2px; +mvCaptionBg: #11111180; +mvCaptionFont: font(fsize); + medviewSaveMsgCheck: sprite(311px, 309px, 22px, 18px); medviewSaveMsgFont: font(16px); medviewSaveMsgPadding: margins(55px, 19px, 29px, 20px); medviewSaveMsgCheckPos: point(23px, 21px); -medviewSaveMsgRadius: 3px; medviewSaveMsgShowing: 200; medviewSaveMsgShown: 2000; medviewSaveMsgHiding: 2500; diff --git a/Telegram/Resources/style_classes.txt b/Telegram/Resources/style_classes.txt index f07d13cef..07a7c5bad 100644 --- a/Telegram/Resources/style_classes.txt +++ b/Telegram/Resources/style_classes.txt @@ -20,7 +20,7 @@ textStyle { lnkOverFlags: font; lnkColor: color; lnkDownColor: color; - selectBG: color; + selectBg: color; selectOverlay: color; lineHeight: number; } diff --git a/Telegram/SourceFiles/app.cpp b/Telegram/SourceFiles/app.cpp index b3088bfd4..da9550e1e 100644 --- a/Telegram/SourceFiles/app.cpp +++ b/Telegram/SourceFiles/app.cpp @@ -75,6 +75,9 @@ namespace { QPixmap *sprite = 0, *emojis = 0, *emojisLarge = 0; + QPixmap *corners[RoundCornersCount][4] = { { 0 } }; + QImage *cornersMask[4] = { 0 }; + typedef QMap EmojisMap; EmojisMap mainEmojisMap; QMap otherEmojisMap; @@ -86,7 +89,8 @@ namespace { typedef QHash LastPhotosMap; LastPhotosMap lastPhotosMap; - style::color _msgServiceBG; + style::color _msgServiceBg; + style::color _msgServiceSelectBg; style::color _historyScrollBarColor; style::color _historyScrollBgColor; style::color _historyScrollBarOverColor; @@ -934,7 +938,11 @@ namespace App { WebPageData *feedWebPage(const MTPWebPage &webpage) { switch (webpage.type()) { case mtpc_webPage: return App::feedWebPage(webpage.c_webPage()); - case mtpc_webPageEmpty: return App::webPage(webpage.c_webPageEmpty().vid.v); + case mtpc_webPageEmpty: { + WebPageData *page = App::webPage(webpage.c_webPageEmpty().vid.v); + if (page->pendingTill > 0) page->pendingTill = -1; // failed + return page; + } break; case mtpc_webPagePending: return App::feedWebPage(webpage.c_webPagePending()); } return 0; @@ -1572,6 +1580,34 @@ namespace App { return 0; } + void prepareCorners(RoundCorners index, int32 radius, const style::color &color, const style::color *shadow = 0, QImage *cors = 0) { + int32 r = radius * cIntRetinaFactor(), s = st::msgShadow * cIntRetinaFactor(); + QImage rect(r * 3, r * 3 + (shadow ? s : 0), QImage::Format_ARGB32_Premultiplied), localCors[4]; + { + QPainter p(&rect); + p.setCompositionMode(QPainter::CompositionMode_Source); + p.fillRect(QRect(0, 0, rect.width(), rect.height()), st::transparent->b); + p.setCompositionMode(QPainter::CompositionMode_SourceOver); + p.setRenderHint(QPainter::HighQualityAntialiasing); + p.setPen(Qt::NoPen); + if (shadow) { + p.setBrush((*shadow)->b); + p.drawRoundedRect(0, s, r * 3, r * 3, r, r); + } + p.setBrush(color->b); + p.drawRoundedRect(0, 0, r * 3, r * 3, r, r); + } + if (!cors) cors = localCors; + cors[0] = rect.copy(0, 0, r, r); + cors[1] = rect.copy(r * 2, 0, r, r); + cors[2] = rect.copy(0, r * 2, r, r + (shadow ? s : 0)); + cors[3] = rect.copy(r * 2, r * 2, r, r + (shadow ? s : 0)); + for (int i = 0; i < 4; ++i) { + ::corners[index][i] = new QPixmap(QPixmap::fromImage(cors[i], Qt::ColorOnly)); + ::corners[index][i]->setDevicePixelRatio(cRetinaFactor()); + } + } + void initMedia() { deinitMedia(false); audioInit(); @@ -1593,6 +1629,33 @@ namespace App { ::emojisLarge = new QPixmap(QLatin1String(EmojiNames[EIndex + 1])); if (cRetina()) ::emojisLarge->setDevicePixelRatio(cRetinaFactor()); } + + QImage mask[4]; + prepareCorners(MaskCorners, st::msgRadius, st::white, 0, mask); + for (int i = 0; i < 4; ++i) { + ::cornersMask[i] = new QImage(mask[i]); + ::cornersMask[i]->convertToFormat(QImage::Format_ARGB32_Premultiplied); + ::cornersMask[i]->setDevicePixelRatio(cRetinaFactor()); + } + prepareCorners(BlackCorners, st::msgRadius, st::black); + prepareCorners(ServiceCorners, st::msgRadius, st::msgServiceBg); + prepareCorners(ServiceSelectedCorners, st::msgRadius, st::msgServiceSelectBg); + prepareCorners(SelectedOverlayCorners, st::msgRadius, st::msgSelectOverlay); + prepareCorners(DateCorners, st::msgRadius, st::msgDateImgBg); + prepareCorners(DateSelectedCorners, st::msgRadius, st::msgDateImgSelectBg); + prepareCorners(InShadowCorners, st::msgRadius, st::msgInShadow); + prepareCorners(InSelectedShadowCorners, st::msgRadius, st::msgInSelectShadow); + prepareCorners(ForwardCorners, st::msgRadius, st::emojiPanHover); + prepareCorners(MediaviewSaveCorners, st::msgRadius, st::emojiPanHover); + prepareCorners(EmojiHoverCorners, st::msgRadius, st::emojiPanHover); + prepareCorners(StickerHoverCorners, st::msgRadius, st::emojiPanHover); + + prepareCorners(MessageInCorners, st::msgRadius, st::msgInBg, &st::msgInShadow); + prepareCorners(MessageInSelectedCorners, st::msgRadius, st::msgInSelectBg, &st::msgInSelectShadow); + prepareCorners(MessageOutCorners, st::msgRadius, st::msgOutBg, &st::msgOutShadow); + prepareCorners(MessageOutSelectedCorners, st::msgRadius, st::msgOutSelectBg, &st::msgOutSelectShadow); + prepareCorners(ButtonHoverCorners, st::msgRadius, st::mediaSaveButton.overBgColor, &st::msgInShadow); + } void deinitMedia(bool completely) { @@ -1610,6 +1673,12 @@ namespace App { ::emojis = 0; delete ::emojisLarge; ::emojisLarge = 0; + for (int32 j = 0; j < 4; ++j) { + for (int32 i = 0; i < RoundCornersCount; ++i) { + delete ::corners[i][j]; ::corners[i][j] = 0; + } + delete ::cornersMask[j]; ::cornersMask[j] = 0; + } mainEmojisMap.clear(); otherEmojisMap.clear(); @@ -1922,6 +1991,31 @@ namespace App { } } + QImage **cornersMask() { + return ::cornersMask; + } + QPixmap **corners(RoundCorners index) { + return ::corners[index]; + } + + void roundRect(QPainter &p, int32 x, int32 y, int32 w, int32 h, const style::color &bg, RoundCorners index, const style::color *sh) { + QPixmap **c = ::corners[index]; + int32 cw = c[0]->width() / cIntRetinaFactor(), ch = c[0]->height() / cIntRetinaFactor(); + if (w < 2 * cw || h < 2 * ch) return; + if (w > 2 * cw) { + p.fillRect(QRect(x + cw, y, w - 2 * cw, ch), bg->b); + p.fillRect(QRect(x + cw, y + h - ch, w - 2 * cw, ch), bg->b); + if (sh) p.fillRect(QRect(x + cw, y + h, w - 2 * cw, st::msgShadow), (*sh)->b); + } + if (h > 2 * ch) { + p.fillRect(QRect(x, y + ch, w, h - 2 * ch), bg->b); + } + p.drawPixmap(QPoint(x, y), *c[0]); + p.drawPixmap(QPoint(x + w - cw, y), *c[1]); + p.drawPixmap(QPoint(x, y + h - ch), *c[2]); + p.drawPixmap(QPoint(x + w - cw, y + h - ch), *c[3]); + } + void initBackground(int32 id, const QImage &p, bool nowrite) { if (Local::readBackground()) return; @@ -2064,7 +2158,17 @@ namespace App { components[maxtomin[0]] = max; uchar r = uchar(components[0]), g = uchar(components[1]), b = uchar(components[2]); - _msgServiceBG = style::color(r, g, b, qRound(st::msgServiceBG->c.alphaF() * 0xFF)); + float64 alpha = st::msgServiceBg->c.alphaF(); + _msgServiceBg = style::color(r, g, b, qRound(alpha * 0xFF)); + + float64 alphaSel = alphaSel = st::msgServiceSelectBg->c.alphaF(), addSel = (1. - ((1. - alphaSel) / (1. - alpha))) * 0xFF; + uchar rsel = snap(qRound(((1. - alphaSel) * r + addSel) / alphaSel), 0, 0xFF); + uchar gsel = snap(qRound(((1. - alphaSel) * g + addSel) / alphaSel), 0, 0xFF); + uchar bsel = snap(qRound(((1. - alphaSel) * b + addSel) / alphaSel), 0, 0xFF); + _msgServiceSelectBg = style::color(r, g, b, qRound(alphaSel * 0xFF)); + + prepareCorners(ServiceCorners, st::msgRadius, _msgServiceBg); + prepareCorners(ServiceSelectedCorners, st::msgRadius, _msgServiceSelectBg); uchar rScroll = uchar(componentsScroll[0]), gScroll = uchar(componentsScroll[1]), bScroll = uchar(componentsScroll[2]); _historyScrollBarColor = style::color(rScroll, gScroll, bScroll, qRound(st::historyScroll.barColor->c.alphaF() * 0xFF)); @@ -2077,8 +2181,12 @@ namespace App { if (App::main()) App::main()->updateScrollColors(); } - style::color msgServiceBG() { - return _msgServiceBG; + style::color msgServiceBg() { + return _msgServiceBg; + } + + style::color msgServiceSelectBg() { + return _msgServiceSelectBg; } style::color historyScrollBarColor() { diff --git a/Telegram/SourceFiles/app.h b/Telegram/SourceFiles/app.h index 5dc0ce6a2..5fea933b7 100644 --- a/Telegram/SourceFiles/app.h +++ b/Telegram/SourceFiles/app.h @@ -36,6 +36,31 @@ typedef QHash AudioItems; typedef QHash DocumentItems; typedef QHash WebPageItems; +enum RoundCorners { + MaskCorners = 0x00, // for images + BlackCorners, + ServiceCorners, + ServiceSelectedCorners, + SelectedOverlayCorners, + DateCorners, + DateSelectedCorners, + ForwardCorners, + MediaviewSaveCorners, + EmojiHoverCorners, + StickerHoverCorners, + + InShadowCorners, // for photos without bg + InSelectedShadowCorners, + + MessageInCorners, // with shadow + MessageInSelectedCorners, + MessageOutCorners, + MessageOutSelectedCorners, + ButtonHoverCorners, + + RoundCornersCount +}; + namespace App { Application *app(); Window *wnd(); @@ -205,9 +230,17 @@ namespace App { void stickersBox(const QString &name); void openLocalUrl(const QString &url); + QImage **cornersMask(); + QPixmap **corners(RoundCorners index); + void roundRect(QPainter &p, int32 x, int32 y, int32 w, int32 h, const style::color &bg, RoundCorners index, const style::color *sh = 0); + inline void roundRect(QPainter &p, const QRect &rect, const style::color &bg, RoundCorners index, const style::color *sh = 0) { + return roundRect(p, rect.x(), rect.y(), rect.width(), rect.height(), bg, index, sh); + } + void initBackground(int32 id = DefaultChatBackground, const QImage &p = QImage(), bool nowrite = false); - style::color msgServiceBG(); + style::color msgServiceBg(); + style::color msgServiceSelectBg(); style::color historyScrollBarColor(); style::color historyScrollBgColor(); style::color historyScrollBarOverColor(); diff --git a/Telegram/SourceFiles/art/sprite.png b/Telegram/SourceFiles/art/sprite.png index efc99191c..1b89b1136 100644 Binary files a/Telegram/SourceFiles/art/sprite.png and b/Telegram/SourceFiles/art/sprite.png differ diff --git a/Telegram/SourceFiles/art/sprite_200x.png b/Telegram/SourceFiles/art/sprite_200x.png index f36cff458..63d898be0 100644 Binary files a/Telegram/SourceFiles/art/sprite_200x.png and b/Telegram/SourceFiles/art/sprite_200x.png differ diff --git a/Telegram/SourceFiles/boxes/photosendbox.cpp b/Telegram/SourceFiles/boxes/photosendbox.cpp index 212be8a05..7f3a408da 100644 --- a/Telegram/SourceFiles/boxes/photosendbox.cpp +++ b/Telegram/SourceFiles/boxes/photosendbox.cpp @@ -147,8 +147,7 @@ void PhotoSendBox::paintEvent(QPaintEvent *e) { } int32 x = (width() - w) / 2, y = st::boxPadding.top() * 2 + st::boxFont->height; - p.fillRect(QRect(x, y, w, h), st::msgOutBG->b); - p.fillRect(x, y + h, w, st::msgShadow, st::msgOutShadow->b); + App::roundRect(p, x, y, w, h, st::msgOutBg, MessageOutCorners, &st::msgOutShadow); if (_thumbw) { int32 rf(cIntRetinaFactor()); p.drawPixmap(QPoint(x + st::mediaPadding.left(), y + st::mediaPadding.top()), _thumb, QRect(_thumbx * rf, _thumby * rf, st::mediaThumbSize * rf, st::mediaThumbSize * rf)); diff --git a/Telegram/SourceFiles/boxes/stickersetbox.cpp b/Telegram/SourceFiles/boxes/stickersetbox.cpp index 778355aa8..a77b7a1a5 100644 --- a/Telegram/SourceFiles/boxes/stickersetbox.cpp +++ b/Telegram/SourceFiles/boxes/stickersetbox.cpp @@ -144,7 +144,7 @@ void StickerSetInner::paintEvent(QPaintEvent *e) { } } - float64 coef = qMin((st::stickersSize.width() - st::stickerPanRound * 2) / float64(doc->dimensions.width()), (st::stickersSize.height() - st::stickerPanRound * 2) / float64(doc->dimensions.height())); + float64 coef = qMin((st::stickersSize.width() - st::msgRadius * 2) / float64(doc->dimensions.width()), (st::stickersSize.height() - st::msgRadius * 2) / float64(doc->dimensions.height())); if (coef > 1) coef = 1; int32 w = qRound(coef * doc->dimensions.width()), h = qRound(coef * doc->dimensions.height()); if (w < 1) w = 1; diff --git a/Telegram/SourceFiles/dropdown.cpp b/Telegram/SourceFiles/dropdown.cpp index c6f46eaf2..31b4fb438 100644 --- a/Telegram/SourceFiles/dropdown.cpp +++ b/Telegram/SourceFiles/dropdown.cpp @@ -675,11 +675,9 @@ void EmojiColorPicker::drawVariant(Painter &p, int variant) { QPoint w(st::dropdownDef.shadow.pxWidth() + st::emojiColorsPadding + variant * st::emojiPanSize.width() + (variant ? 2 * st::emojiColorsPadding + st::emojiColorsSep : 0), st::dropdownDef.shadow.pxHeight() + st::emojiColorsPadding); if (hover > 0) { p.setOpacity(hover); - p.setBrush(st::emojiPanHover->b); - p.setPen(Qt::NoPen); QPoint tl(w); if (rtl()) tl.setX(width() - tl.x() - st::emojiPanSize.width()); - p.drawRoundedRect(QRect(tl, st::emojiPanSize), st::emojiPanRound, st::emojiPanRound); + App::roundRect(p, QRect(tl, st::emojiPanSize), st::emojiPanHover, StickerHoverCorners); p.setOpacity(1); } int esize = EmojiSizes[EIndex + 1]; @@ -786,11 +784,9 @@ void EmojiPanInner::paintEvent(QPaintEvent *e) { QPoint w(st::emojiPanPadding + j * st::emojiPanSize.width(), y + i * st::emojiPanSize.height()); if (hover > 0) { p.setOpacity(hover); - p.setBrush(st::emojiPanHover->b); - p.setPen(Qt::NoPen); QPoint tl(w); if (rtl()) tl.setX(width() - tl.x() - st::emojiPanSize.width()); - p.drawRoundedRect(QRect(tl, st::emojiPanSize), st::emojiPanRound, st::emojiPanRound); + App::roundRect(p, QRect(tl, st::emojiPanSize), st::emojiPanHover, StickerHoverCorners); p.setOpacity(1); } p.drawPixmapLeft(w.x() + (st::emojiPanSize.width() - (_esize / cIntRetinaFactor())) / 2, w.y() + (st::emojiPanSize.height() - (_esize / cIntRetinaFactor())) / 2, width(), App::emojisLarge(), QRect(_emojis[c][index]->x * _esize, _emojis[c][index]->y * _esize, _esize, _esize)); @@ -928,9 +924,9 @@ void EmojiPanInner::onShowPicker() { int32 size = (c == tab) ? (sel - (sel % EmojiPanPerRow)) : _counts[c], rows = (size / EmojiPanPerRow) + ((size % EmojiPanPerRow) ? 1 : 0); y += st::emojiPanHeader + (rows * st::emojiPanSize.height()); } - y -= _picker.height() - st::emojiPanRound; + y -= _picker.height() - st::msgRadius; if (y < _top) { - y += _picker.height() - st::emojiPanRound + st::emojiPanSize.height() - st::emojiPanRound; + y += _picker.height() - st::msgRadius + st::emojiPanSize.height() - st::msgRadius; } int xmax = width() - _picker.width(); float64 coef = float64(sel % EmojiPanPerRow) / float64(EmojiPanPerRow - 1); @@ -1211,11 +1207,9 @@ void StickerPanInner::paintEvent(QPaintEvent *e) { QPoint pos(st::stickerPanPadding + j * st::stickerPanSize.width(), y + i * st::stickerPanSize.height()); if (hover > 0) { p.setOpacity(hover); - p.setBrush(st::emojiPanHover->b); - p.setPen(Qt::NoPen); QPoint tl(pos); if (rtl()) tl.setX(width() - tl.x() - st::stickerPanSize.width()); - p.drawRoundedRect(QRect(tl, st::stickerPanSize), st::stickerPanRound, st::stickerPanRound); + App::roundRect(p, QRect(tl, st::stickerPanSize), st::emojiPanHover, StickerHoverCorners); p.setOpacity(1); } @@ -1236,7 +1230,7 @@ void StickerPanInner::paintEvent(QPaintEvent *e) { } } - float64 coef = qMin((st::stickerPanSize.width() - st::stickerPanRound * 2) / float64(sticker->dimensions.width()), (st::stickerPanSize.height() - st::stickerPanRound * 2) / float64(sticker->dimensions.height())); + float64 coef = qMin((st::stickerPanSize.width() - st::msgRadius * 2) / float64(sticker->dimensions.width()), (st::stickerPanSize.height() - st::msgRadius * 2) / float64(sticker->dimensions.height())); if (coef > 1) coef = 1; int32 w = qRound(coef * sticker->dimensions.width()), h = qRound(coef * sticker->dimensions.height()); if (w < 1) w = 1; @@ -2012,9 +2006,9 @@ void EmojiPan::onSwitch() { hideAll(); _moveStart = getms(); - a_toCoord = _stickersShown ? anim::ivalue(st::emojiPanFullSize.width(), 0) : anim::ivalue(-st::emojiPanFullSize.width(), 0); + a_toCoord = (_stickersShown != rtl()) ? anim::ivalue(st::emojiPanFullSize.width(), 0) : anim::ivalue(-st::emojiPanFullSize.width(), 0); a_toAlpha = anim::fvalue(0, 1); - a_fromCoord = _stickersShown ? anim::ivalue(0, -st::emojiPanFullSize.width()) : anim::ivalue(0, st::emojiPanFullSize.width()); + a_fromCoord = (_stickersShown != rtl()) ? anim::ivalue(0, -st::emojiPanFullSize.width()) : anim::ivalue(0, st::emojiPanFullSize.width()); a_fromAlpha = anim::fvalue(1, 0); if (!animating()) anim::start(this); @@ -2110,7 +2104,7 @@ void MentionsInner::paintEvent(QPaintEvent *e) { } } user->photo->load(); - p.drawPixmap(st::mentionPadding.left(), i * st::mentionHeight + st::mentionPadding.top(), user->photo->pix(st::mentionPhotoSize)); + p.drawPixmap(st::mentionPadding.left(), i * st::mentionHeight + st::mentionPadding.top(), user->photo->pixRounded(st::mentionPhotoSize)); user->nameText.drawElided(p, 2 * st::mentionPadding.left() + st::mentionPhotoSize, i * st::mentionHeight + st::mentionTop, namewidth); p.setFont(st::mentionFont->f); diff --git a/Telegram/SourceFiles/gui/images.cpp b/Telegram/SourceFiles/gui/images.cpp index 00415df95..067e98aad 100644 --- a/Telegram/SourceFiles/gui/images.cpp +++ b/Telegram/SourceFiles/gui/images.cpp @@ -34,6 +34,11 @@ namespace { StorageImages storageImages; int64 globalAquiredSize = 0; + + static const uint64 BlurredCacheSkip = 0x1000000000000000LLU; + static const uint64 ColoredCacheSkip = 0x2000000000000000LLU; + static const uint64 BlurredColoredCacheSkip = 0x3000000000000000LLU; + static const uint64 RoundedCacheSkip = 0x4000000000000000LLU; } bool Image::isNull() const { @@ -70,6 +75,29 @@ const QPixmap &Image::pix(int32 w, int32 h) const { return i.value(); } +const QPixmap &Image::pixRounded(int32 w, int32 h) const { + restore(); + checkload(); + + if (w <= 0 || !width() || !height()) { + w = width(); + } else if (cRetina()) { + w *= cIntRetinaFactor(); + h *= cIntRetinaFactor(); + } + uint64 k = RoundedCacheSkip | (uint64(w) << 32) | uint64(h); + Sizes::const_iterator i = _sizesCache.constFind(k); + if (i == _sizesCache.cend()) { + QPixmap p(pixNoCache(w, h, true, false, true)); + if (cRetina()) p.setDevicePixelRatio(cRetinaFactor()); + i = _sizesCache.insert(k, p); + if (!p.isNull()) { + globalAquiredSize += int64(p.width()) * p.height() * 4; + } + } + return i.value(); +} + const QPixmap &Image::pixBlurred(int32 w, int32 h) const { restore(); checkload(); @@ -80,10 +108,10 @@ const QPixmap &Image::pixBlurred(int32 w, int32 h) const { w *= cIntRetinaFactor(); h *= cIntRetinaFactor(); } - uint64 k = 0x1000000000000000LL | (uint64(w) << 32) | uint64(h); + uint64 k = BlurredCacheSkip | (uint64(w) << 32) | uint64(h); Sizes::const_iterator i = _sizesCache.constFind(k); if (i == _sizesCache.cend()) { - QPixmap p(pixBlurredNoCache(w, h)); + QPixmap p(pixNoCache(w, h, true, true)); if (cRetina()) p.setDevicePixelRatio(cRetinaFactor()); i = _sizesCache.insert(k, p); if (!p.isNull()) { @@ -103,7 +131,7 @@ const QPixmap &Image::pixColored(const style::color &add, int32 w, int32 h) cons w *= cIntRetinaFactor(); h *= cIntRetinaFactor(); } - uint64 k = 0x2000000000000000LL | (uint64(w) << 32) | uint64(h); + uint64 k = ColoredCacheSkip | (uint64(w) << 32) | uint64(h); Sizes::const_iterator i = _sizesCache.constFind(k); if (i == _sizesCache.cend()) { QPixmap p(pixColoredNoCache(add, w, h, true)); @@ -126,7 +154,7 @@ const QPixmap &Image::pixBlurredColored(const style::color &add, int32 w, int32 w *= cIntRetinaFactor(); h *= cIntRetinaFactor(); } - uint64 k = 0x3000000000000000LL | (uint64(w) << 32) | uint64(h); + uint64 k = BlurredColoredCacheSkip | (uint64(w) << 32) | uint64(h); Sizes::const_iterator i = _sizesCache.constFind(k); if (i == _sizesCache.cend()) { QPixmap p(pixBlurredColoredNoCache(add, w, h)); @@ -139,7 +167,7 @@ const QPixmap &Image::pixBlurredColored(const style::color &add, int32 w, int32 return i.value(); } -const QPixmap &Image::pixSingle(int32 w, int32 h) const { +const QPixmap &Image::pixSingle(int32 w, int32 h, int32 outerw, int32 outerh) const { restore(); checkload(); @@ -155,7 +183,7 @@ const QPixmap &Image::pixSingle(int32 w, int32 h) const { if (i != _sizesCache.cend()) { globalAquiredSize -= int64(i->width()) * i->height() * 4; } - QPixmap p(pixNoCache(w, h, true)); + QPixmap p(pixNoCache(w, h, true, false, true, outerw, outerh)); if (cRetina()) p.setDevicePixelRatio(cRetinaFactor()); i = _sizesCache.insert(k, p); if (!p.isNull()) { @@ -165,7 +193,7 @@ const QPixmap &Image::pixSingle(int32 w, int32 h) const { return i.value(); } -const QPixmap &Image::pixBlurredSingle(int32 w, int32 h) const { +const QPixmap &Image::pixBlurredSingle(int32 w, int32 h, int32 outerw, int32 outerh) const { restore(); checkload(); @@ -175,13 +203,13 @@ const QPixmap &Image::pixBlurredSingle(int32 w, int32 h) const { w *= cIntRetinaFactor(); h *= cIntRetinaFactor(); } - uint64 k = 0x1000000000000000LL | 0LL; + uint64 k = BlurredCacheSkip | 0LL; Sizes::const_iterator i = _sizesCache.constFind(k); if (i == _sizesCache.cend() || i->width() != w || (h && i->height() != h)) { if (i != _sizesCache.cend()) { globalAquiredSize -= int64(i->width()) * i->height() * 4; } - QPixmap p(pixBlurredNoCache(w, h)); + QPixmap p(pixNoCache(w, h, true, true, true, outerw, outerh)); if (cRetina()) p.setDevicePixelRatio(cRetinaFactor()); i = _sizesCache.insert(k, p); if (!p.isNull()) { @@ -309,6 +337,39 @@ yi += stride; return img; } +void imageRound(QImage &img) { + img.setDevicePixelRatio(cRetinaFactor()); + img = img.convertToFormat(QImage::Format_ARGB32_Premultiplied); + + QImage **masks = App::cornersMask(); + int32 w = masks[0]->width(), h = masks[0]->height(); + int32 tw = img.width(), th = img.height(); + + uchar *bits = img.bits(); + const uchar *c0 = masks[0]->constBits(), *c1 = masks[1]->constBits(), *c2 = masks[2]->constBits(), *c3 = masks[3]->constBits(); + + int32 s0 = 0, s1 = (tw - w) * 4, s2 = (th - h) * tw * 4, s3 = ((th - h + 1) * tw - w) * 4; + for (int32 i = 0; i < w; ++i) { + for (int32 j = 0; j < h; ++j) { +#define update(s, c) \ + { \ + uint64 color = _blurGetColors(bits + s + (j * tw + i) * 4); \ + color *= (c[(j * w + i) * 4 + 3] + 1); \ + color = (color >> 8); \ + bits[s + (j * tw + i) * 4] = color & 0xFF; \ + bits[s + (j * tw + i) * 4 + 1] = (color >> 16) & 0xFF; \ + bits[s + (j * tw + i) * 4 + 2] = (color >> 32) & 0xFF; \ + bits[s + (j * tw + i) * 4 + 3] = (color >> 48) & 0xFF; \ + } + update(s0, c0); + update(s1, c1); + update(s2, c2); + update(s3, c3); +#undef update + } + } +} + QImage imageColored(const style::color &add, QImage img) { QImage::Format fmt = img.format(); if (fmt != QImage::Format_RGB32 && fmt != QImage::Format_ARGB32_Premultiplied) { @@ -330,35 +391,39 @@ QImage imageColored(const style::color &add, QImage img) { return img; } -QPixmap Image::pixNoCache(int32 w, int32 h, bool smooth) const { - restore(); - loaded(); - - const QPixmap &p(pixData()); - if (p.isNull()) { - return blank()->pix(); - } - if (w <= 0 || !width() || !height() || (w == width() && (h <= 0 || h == height()))) return p; - if (h <= 0) { - return QPixmap::fromImage(p.toImage().scaledToWidth(w, smooth ? Qt::SmoothTransformation : Qt::FastTransformation), Qt::ColorOnly); - } - return QPixmap::fromImage(p.toImage().scaled(w, h, Qt::IgnoreAspectRatio, smooth ? Qt::SmoothTransformation : Qt::FastTransformation), Qt::ColorOnly); -} - -QPixmap Image::pixBlurredNoCache(int32 w, int32 h) const { +QPixmap Image::pixNoCache(int32 w, int32 h, bool smooth, bool blurred, bool rounded, int32 outerw, int32 outerh) const { restore(); loaded(); const QPixmap &p(pixData()); if (p.isNull()) return blank()->pix(); - QImage img = imageBlur(p.toImage()); - if (h <= 0) { - img = img.scaledToWidth(w, Qt::SmoothTransformation); + QImage img = p.toImage(); + if (blurred) img = imageBlur(img); + if (w <= 0 || !width() || !height() || (w == width() && (h <= 0 || h == height()))) { + } else if (h <= 0) { + img = img.scaledToWidth(w, smooth ? Qt::SmoothTransformation : Qt::FastTransformation); } else { - img = img.scaled(w, h, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + img = img.scaled(w, h, Qt::IgnoreAspectRatio, smooth ? Qt::SmoothTransformation : Qt::FastTransformation); } - + if (outerw > 0 && outerh > 0) { + outerw *= cIntRetinaFactor(); + outerh *= cIntRetinaFactor(); + if (outerw != w || outerh != h) { + img.setDevicePixelRatio(cRetinaFactor()); + QImage result(outerw, outerh, QImage::Format_ARGB32_Premultiplied); + result.setDevicePixelRatio(cRetinaFactor()); + { + QPainter p(&result); + if (w < outerw || h < outerh) { + p.fillRect(0, 0, result.width(), result.height(), st::black->b); + } + p.drawImage((result.width() - img.width()) / 2, (result.height() - img.height()) / 2, img); + } + img = result; + } + } + if (rounded) imageRound(img); return QPixmap::fromImage(img, Qt::ColorOnly); } diff --git a/Telegram/SourceFiles/gui/images.h b/Telegram/SourceFiles/gui/images.h index 6d4a437aa..f74e7191f 100644 --- a/Telegram/SourceFiles/gui/images.h +++ b/Telegram/SourceFiles/gui/images.h @@ -47,13 +47,13 @@ public: return false; } const QPixmap &pix(int32 w = 0, int32 h = 0) const; + const QPixmap &pixRounded(int32 w = 0, int32 h = 0) const; const QPixmap &pixBlurred(int32 w = 0, int32 h = 0) const; const QPixmap &pixColored(const style::color &add, int32 w = 0, int32 h = 0) const; const QPixmap &pixBlurredColored(const style::color &add, int32 w = 0, int32 h = 0) const; - const QPixmap &pixSingle(int32 w = 0, int32 h = 0) const; - const QPixmap &pixBlurredSingle(int32 w = 0, int32 h = 0) const; - QPixmap pixNoCache(int32 w = 0, int32 h = 0, bool smooth = false) const; - QPixmap pixBlurredNoCache(int32 w, int32 h = 0) const; + const QPixmap &pixSingle(int32 w, int32 y, int32 outerw, int32 outerh) const; + const QPixmap &pixBlurredSingle(int32 w, int32 h, int32 outerw, int32 outerh) const; + QPixmap pixNoCache(int32 w = 0, int32 h = 0, bool smooth = false, bool blurred = false, bool rounded = false, int32 outerw = -1, int32 outerh = -1) const; QPixmap pixColoredNoCache(const style::color &add, int32 w = 0, int32 h = 0, bool smooth = false) const; QPixmap pixBlurredColoredNoCache(const style::color &add, int32 w, int32 h = 0) const; diff --git a/Telegram/SourceFiles/gui/text.cpp b/Telegram/SourceFiles/gui/text.cpp index d41fad940..ceda2a487 100644 --- a/Telegram/SourceFiles/gui/text.cpp +++ b/Telegram/SourceFiles/gui/text.cpp @@ -1129,12 +1129,12 @@ public: if ((selectFromStart && _parDirection == Qt::LeftToRight) || (selectTillEnd && _parDirection == Qt::RightToLeft)) { if (x > _x) { - _p->fillRect(QRectF(_x.toReal(), _y + _yDelta, (x - _x).toReal(), _fontHeight), _textStyle->selectBG->b); + _p->fillRect(QRectF(_x.toReal(), _y + _yDelta, (x - _x).toReal(), _fontHeight), _textStyle->selectBg->b); } } if ((selectTillEnd && _parDirection == Qt::LeftToRight) || (selectFromStart && _parDirection == Qt::RightToLeft)) { if (x < _x + _wLeft) { - _p->fillRect(QRectF((x + _w - _wLeft).toReal(), _y + _yDelta, (_x + _wLeft - x).toReal(), _fontHeight), _textStyle->selectBG->b); + _p->fillRect(QRectF((x + _w - _wLeft).toReal(), _y + _yDelta, (_x + _wLeft - x).toReal(), _fontHeight), _textStyle->selectBg->b); } } @@ -1292,15 +1292,15 @@ public: const QChar *chFrom = _str + currentBlock->from(), *chTo = chFrom + ((nextBlock ? nextBlock->from() : _t->_text.size()) - currentBlock->from()); if (_localFrom + si.position >= _selectedFrom) { // could be without space if (chTo == chFrom || (chTo - 1)->unicode() != QChar::Space || _selectedTo >= (chTo - _str)) { - _p->fillRect(QRectF(x.toReal(), _y + _yDelta, si.width.toReal(), _fontHeight), _textStyle->selectBG->b); + _p->fillRect(QRectF(x.toReal(), _y + _yDelta, si.width.toReal(), _fontHeight), _textStyle->selectBg->b); } else { // or with space - _p->fillRect(QRectF(glyphX.toReal(), _y + _yDelta, currentBlock->f_width().toReal(), _fontHeight), _textStyle->selectBG->b); + _p->fillRect(QRectF(glyphX.toReal(), _y + _yDelta, currentBlock->f_width().toReal(), _fontHeight), _textStyle->selectBg->b); } } else if (chTo > chFrom && (chTo - 1)->unicode() == QChar::Space && (chTo - 1 - _str) >= _selectedFrom) { if (rtl) { // rtl space only - _p->fillRect(QRectF(x.toReal(), _y + _yDelta, (glyphX - x).toReal(), _fontHeight), _textStyle->selectBG->b); + _p->fillRect(QRectF(x.toReal(), _y + _yDelta, (glyphX - x).toReal(), _fontHeight), _textStyle->selectBg->b); } else { // ltr space only - _p->fillRect(QRectF((x + currentBlock->f_width()).toReal(), _y + _yDelta, (si.width - currentBlock->f_width()).toReal(), _fontHeight), _textStyle->selectBG->b); + _p->fillRect(QRectF((x + currentBlock->f_width()).toReal(), _y + _yDelta, (si.width - currentBlock->f_width()).toReal(), _fontHeight), _textStyle->selectBg->b); } } } @@ -1426,7 +1426,7 @@ public: } } if (rtl) selX = x + itemWidth - (selX - x) - selWidth; - _p->fillRect(QRectF(selX.toReal(), _y + _yDelta, selWidth.toReal(), _fontHeight), _textStyle->selectBG->b); + _p->fillRect(QRectF(selX.toReal(), _y + _yDelta, selWidth.toReal(), _fontHeight), _textStyle->selectBg->b); } _p->drawTextItem(QPointF(x.toReal(), textY), gf); @@ -2243,6 +2243,21 @@ _startDir(other._startDir) } } +Text &Text::operator=(const Text &other) { + _minResizeWidth = other._minResizeWidth; + _maxWidth = other._maxWidth; + _minHeight = other._minHeight; + _text = other._text; + _font = other._font; + _blocks = TextBlocks(other._blocks.size()); + _links = other._links; + _startDir = other._startDir; + for (int32 i = 0, l = _blocks.size(); i < l; ++i) { + _blocks[i] = other._blocks.at(i)->clone(); + } + return *this; +} + void Text::setText(style::font font, const QString &text, const TextParseOptions &options) { if (!_textStyle) _initDefault(); _font = font; diff --git a/Telegram/SourceFiles/gui/text.h b/Telegram/SourceFiles/gui/text.h index 5d72eaf24..64cc56d0f 100644 --- a/Telegram/SourceFiles/gui/text.h +++ b/Telegram/SourceFiles/gui/text.h @@ -426,6 +426,7 @@ public: Text(int32 minResizeWidth = QFIXED_MAX); Text(style::font font, const QString &text, const TextParseOptions &options = _defaultOptions, int32 minResizeWidth = QFIXED_MAX, bool richText = false); Text(const Text &other); + Text &operator=(const Text &other); int32 countHeight(int32 width) const; void setText(style::font font, const QString &text, const TextParseOptions &options = _defaultOptions); @@ -434,6 +435,10 @@ public: void setLink(uint16 lnkIndex, const TextLinkPtr &lnk); bool hasLinks() const; + bool hasSkipBlock() const { + return _blocks.isEmpty() ? false : _blocks.back()->type() == TextBlockSkip; + } + int32 maxWidth() const { return _maxWidth.ceil().toInt(); } diff --git a/Telegram/SourceFiles/history.cpp b/Telegram/SourceFiles/history.cpp index c10cea049..baef5c8d6 100644 --- a/Telegram/SourceFiles/history.cpp +++ b/Telegram/SourceFiles/history.cpp @@ -1597,7 +1597,7 @@ void HistoryPhoto::initDimensions(const HistoryItem *parent) { _minh += st::msgPadding.top() + st::msgNameFont->height; } if (!_caption.isEmpty()) { - _minh += _caption.minHeight(); + _minh += st::webPagePhotoSkip + _caption.minHeight(); } _minh += st::mediaPadding.bottom(); } @@ -1666,6 +1666,10 @@ const QString HistoryPhoto::inHistoryText() const { return qsl("[ ") + lang(lng_in_dlg_photo) + qsl(" ]"); } +const Text &HistoryPhoto::captionForClone() const { + return _caption; +} + bool HistoryPhoto::hasPoint(int32 x, int32 y, const HistoryItem *parent, int32 width) const { if (width < 0) width = w; return (x >= 0 && y >= 0 && x < width && y < _height); @@ -1765,15 +1769,20 @@ void HistoryPhoto::updateFrom(const MTPMessageMedia &media) { void HistoryPhoto::draw(QPainter &p, const HistoryItem *parent, bool selected, int32 width) const { const HistoryReply *reply = toHistoryReply(parent); const HistoryForwarded *fwd = reply ? 0 : toHistoryForwarded(parent); + bool out = parent->out(); + if (width < 0) width = w; int skipx = 0, skipy = 0, height = _height; if (reply || !_caption.isEmpty()) { skipx = st::mediaPadding.left(); - style::color bg(selected ? (parent->out() ? st::msgOutSelectBG : st::msgInSelectBG) : (parent->out() ? st::msgOutBG : st::msgInBG)); - p.fillRect(QRect(0, 0, width, _height), bg->b); + style::color bg(selected ? (out ? st::msgOutSelectBg : st::msgInSelectBg) : (out ? st::msgOutBg : st::msgInBg)); + style::color sh(selected ? (out ? st::msgOutSelectShadow : st::msgInSelectShadow) : (out ? st::msgOutShadow : st::msgInShadow)); + RoundCorners cors(selected ? (out ? MessageOutSelectedCorners : MessageInSelectedCorners) : (out ? MessageOutCorners : MessageInCorners)); + App::roundRect(p, 0, 0, width, _height, bg, cors, &sh); + int replyFrom = 0, fwdFrom = 0; - if (!parent->out() && parent->history()->peer->chat) { + if (!out && parent->history()->peer->chat) { replyFrom = st::msgPadding.top() + st::msgNameFont->height; fwdFrom = st::msgPadding.top() + st::msgNameFont->height; skipy += replyFrom; @@ -1797,20 +1806,25 @@ void HistoryPhoto::draw(QPainter &p, const HistoryItem *parent, bool selected, i if (!_caption.isEmpty()) { height -= st::webPagePhotoSkip + _caption.countHeight(width); } + } else { + QPixmap **cors = App::corners(selected ? InSelectedShadowCorners : InShadowCorners); + int32 cw = cors[0]->width() / cIntRetinaFactor(), ch = cors[0]->height(); + style::color shadow(selected ? st::msgInSelectShadow : st::msgInShadow); + p.fillRect(cw, _height, width - 2 * cw, st::msgShadow, shadow->b); + p.fillRect(0, _height - ch, cw, st::msgShadow, shadow->b); + p.fillRect(width - cw, _height - ch, cw, st::msgShadow, shadow->b); + p.drawPixmap(0, _height - ch + st::msgShadow, *cors[2]); + p.drawPixmap(width - cw, _height - ch + st::msgShadow, *cors[3]); } data->full->load(false, false); - bool out = parent->out(); bool full = data->full->loaded(); QPixmap pix; if (full) { - pix = data->full->pixSingle(pixw, pixh); + pix = data->full->pixSingle(pixw, pixh, width, height); } else { - pix = data->thumb->pixBlurredSingle(pixw, pixh); + pix = data->thumb->pixBlurredSingle(pixw, pixh, width, height); } - if (pixw < width || pixh < height) { - p.fillRect(QRect(skipx, skipy, width, height), st::black->b); - } - p.drawPixmap(QPoint(skipx + (width - pixw) / 2, skipy + (height - pixh) / 2), pix); + p.drawPixmap(skipx, skipy, pix); if (!full) { uint64 dt = itemAnimations().animate(parent, getms()); int32 cnt = int32(st::photoLoaderCnt), period = int32(st::photoLoaderPeriod), t = dt % period, delta = int32(st::photoLoaderDelta); @@ -1833,10 +1847,8 @@ void HistoryPhoto::draw(QPainter &p, const HistoryItem *parent, bool selected, i } if (selected) { - p.fillRect(skipx, skipy, width, height, textstyleCurrent()->selectOverlay->b); + App::roundRect(p, skipx, skipy, width, height, textstyleCurrent()->selectOverlay, SelectedOverlayCorners); } - style::color shadow(selected ? st::msgInSelectShadow : st::msgInShadow); - p.fillRect(0, _height, width + (skipx ? (st::mediaPadding.left() + st::mediaPadding.right()) : 0), st::msgShadow, shadow->b); // date QString time(parent->time()); @@ -1850,10 +1862,8 @@ void HistoryPhoto::draw(QPainter &p, const HistoryItem *parent, bool selected, i int32 dateW = skipx + width - dateX - st::msgDateImgDelta; int32 dateH = skipy + height - dateY - st::msgDateImgDelta; - p.fillRect(dateX, dateY, dateW, dateH, st::msgDateImgBg->b); - if (selected) { - p.fillRect(dateX, dateY, dateW, dateH, textstyleCurrent()->selectOverlay->b); - } + App::roundRect(p, dateX, dateY, dateW, dateH, selected ? st::msgDateImgSelectBg : st::msgDateImgBg, selected ? DateSelectedCorners : DateCorners); + p.setFont(st::msgDateFont->f); p.setPen(st::msgDateImgColor->p); p.drawText(dateX + st::msgDateImgPadding.x(), dateY + st::msgDateImgPadding.y() + st::msgDateFont->ascent, time); @@ -1945,6 +1955,10 @@ HistoryVideo::HistoryVideo(const MTPDvideo &video, const QString &caption, Histo , _dldDone(0) , _uplDone(0) { + if (!caption.isEmpty()) { + _caption.setText(st::msgFont, caption + textcmdSkipBlock(parent->timeWidth(true), st::msgDateFont->height - st::msgDateDelta.y()), _historyTextOptions); + } + _size = formatDurationAndSizeText(data->duration, data->size); if (!_openWithWidth) { @@ -1988,7 +2002,11 @@ void HistoryVideo::initDimensions(const HistoryItem *parent) { } _minh += st::msgServiceNameFont->height; } - _height = _minh; + if (_caption.isEmpty()) { + _height = _minh; + } else { + _minh += st::webPagePhotoSkip + _caption.minHeight(); + } } void HistoryVideo::regItem(HistoryItem *item) { @@ -2042,7 +2060,11 @@ void HistoryVideo::getState(TextLinkPtr &lnk, bool &inText, int32 x, int32 y, co } if (!out) { // draw Download / Save As button - int32 btnw = _buttonWidth, btnh = st::mediaSaveButton.height, btnx = width - _buttonWidth, btny = skipy + (_height - skipy - btnh) / 2; + int32 h = _height; + if (!_caption.isEmpty()) { + h -= st::webPagePhotoSkip + _caption.countHeight(width - _buttonWidth - st::mediaSaveDelta - st::mediaPadding.left() - st::mediaPadding.right()); + } + int32 btnw = _buttonWidth, btnh = st::mediaSaveButton.height, btnx = width - _buttonWidth, btny = skipy + (h - skipy - btnh) / 2; if (x >= btnx && y >= btny && x < btnx + btnw && y < btny + btnh) { lnk = data->loader ? _cancell : _savel; return; @@ -2067,10 +2089,14 @@ void HistoryVideo::getState(TextLinkPtr &lnk, bool &inText, int32 x, int32 y, co } } - if (x >= 0 && y >= skipy && x < width && y < _height && !data->loader && data->access) { + int32 tw = width - st::mediaPadding.left() - st::mediaPadding.right(); + if (x >= st::mediaPadding.left() && y >= skipy + st::mediaPadding.top() && x < st::mediaPadding.left() + tw && y < skipy + st::mediaPadding.top() + st::mediaThumbSize && !data->loader && data->access) { lnk = _openl; return; } + if (!_caption.isEmpty() && x >= st::mediaPadding.left() && x < st::mediaPadding.left() + tw && y >= skipy + st::mediaPadding.top() + st::mediaThumbSize + st::webPagePhotoSkip) { + return _caption.getState(lnk, inText, x - st::mediaPadding.left(), y - skipy - st::mediaPadding.top() - st::mediaThumbSize - st::webPagePhotoSkip, tw); + } } HistoryMedia *HistoryVideo::clone() const { @@ -2110,11 +2136,16 @@ void HistoryVideo::draw(QPainter &p, const HistoryItem *parent, bool selected, i pressed = hovered && ((data->loader ? _cancell : _savel) == textlnkDown()); if (hovered && !pressed && textlnkDown()) hovered = false; - int32 btnw = _buttonWidth, btnh = st::mediaSaveButton.height, btnx = width - _buttonWidth, btny = skipy + (_height - skipy - btnh) / 2; - p.fillRect(QRect(btnx, btny, btnw, btnh), (selected ? st::msgInSelectBG : (hovered ? st::mediaSaveButton.overBgColor : st::mediaSaveButton.bgColor))->b); + int32 h = _height; + if (!_caption.isEmpty()) { + h -= st::webPagePhotoSkip + _caption.countHeight(width - _buttonWidth - st::mediaSaveDelta - st::mediaPadding.left() - st::mediaPadding.right()); + } - style::color shadow(selected ? (out ? st::msgOutSelectShadow : st::msgInSelectShadow) : (out ? st::msgOutShadow : st::msgInShadow)); - p.fillRect(btnx, btny + btnh, btnw, st::msgShadow, shadow->b); + int32 btnw = _buttonWidth, btnh = st::mediaSaveButton.height, btnx = width - _buttonWidth, btny = skipy + (h - skipy - btnh) / 2; + style::color bg(selected ? st::msgInSelectBg : (hovered ? st::mediaSaveButton.overBgColor : st::mediaSaveButton.bgColor)); + style::color sh(selected ? st::msgInSelectShadow : st::msgInShadow); + RoundCorners cors(selected ? MessageInSelectedCorners : (hovered ? ButtonHoverCorners : MessageInCorners)); + App::roundRect(p, btnx, btny, btnw, btnh, bg, cors, &sh); p.setPen((hovered ? st::mediaSaveButton.overColor : st::mediaSaveButton.color)->p); p.setFont(st::mediaSaveButton.font->f); @@ -2124,11 +2155,10 @@ void HistoryVideo::draw(QPainter &p, const HistoryItem *parent, bool selected, i width -= btnw + st::mediaSaveDelta; } - style::color bg(selected ? (out ? st::msgOutSelectBG : st::msgInSelectBG) : (out ? st::msgOutBG : st::msgInBG)); - p.fillRect(QRect(0, 0, width, _height), bg->b); - - style::color shadow(selected ? (out ? st::msgOutSelectShadow : st::msgInSelectShadow) : (out ? st::msgOutShadow : st::msgInShadow)); - p.fillRect(0, _height, width, st::msgShadow, shadow->b); + style::color bg(selected ? (out ? st::msgOutSelectBg : st::msgInSelectBg) : (out ? st::msgOutBg : st::msgInBg)); + style::color sh(selected ? (out ? st::msgOutSelectShadow : st::msgInSelectShadow) : (out ? st::msgOutShadow : st::msgInShadow)); + RoundCorners cors(selected ? (out ? MessageOutSelectedCorners : MessageInSelectedCorners) : (out ? MessageOutCorners : MessageInCorners)); + App::roundRect(p, 0, 0, width, _height, bg, cors, &sh); if (!parent->out() && parent->history()->peer->chat) { p.setFont(st::msgNameFont->f); @@ -2142,13 +2172,12 @@ void HistoryVideo::draw(QPainter &p, const HistoryItem *parent, bool selected, i } if (_thumbw) { - int32 rf(cIntRetinaFactor()); - p.drawPixmap(QPoint(st::mediaPadding.left(), skipy + st::mediaPadding.top()), data->thumb->pix(_thumbw), QRect(_thumbx * rf, _thumby * rf, st::mediaThumbSize * rf, st::mediaThumbSize * rf)); + p.drawPixmap(QPoint(st::mediaPadding.left(), skipy + st::mediaPadding.top()), data->thumb->pixSingle(_thumbw, 0, st::mediaThumbSize, st::mediaThumbSize)); } else { p.drawPixmap(QPoint(st::mediaPadding.left(), skipy + st::mediaPadding.top()), App::sprite(), (out ? st::mediaDocOutImg : st::mediaDocInImg)); } if (selected) { - p.fillRect(st::mediaPadding.left(), skipy + st::mediaPadding.top(), st::mediaThumbSize, st::mediaThumbSize, (out ? st::msgOutSelectOverlay : st::msgInSelectOverlay)->b); + App::roundRect(p, st::mediaPadding.left(), skipy + st::mediaPadding.top(), st::mediaThumbSize, st::mediaThumbSize, textstyleCurrent()->selectOverlay, SelectedOverlayCorners); } int32 tleft = st::mediaPadding.left() + st::mediaThumbSize + st::mediaPadding.right(); @@ -2198,6 +2227,11 @@ void HistoryVideo::draw(QPainter &p, const HistoryItem *parent, bool selected, i } p.setFont(st::msgDateFont->f); + if (!_caption.isEmpty()) { + p.setPen(st::black->p); + _caption.draw(p, st::mediaPadding.left(), skipy + st::mediaPadding.top() + st::mediaThumbSize + st::webPagePhotoSkip, width - st::mediaPadding.left() - st::mediaPadding.right()); + } + style::color date(selected ? (out ? st::msgOutSelectDateColor : st::msgInSelectDateColor) : (out ? st::msgOutDateColor : st::msgInDateColor)); p.setPen(date->p); @@ -2218,6 +2252,32 @@ void HistoryVideo::draw(QPainter &p, const HistoryItem *parent, bool selected, i } } +int32 HistoryVideo::resize(int32 width, bool dontRecountText, const HistoryItem *parent) { + w = qMin(width, _maxw); + if (_caption.isEmpty()) return _height; + + _height = st::mediaPadding.top() + st::mediaThumbSize + st::mediaPadding.bottom(); + if (!parent->out() && parent->history()->peer->chat) { + _height += st::msgPadding.top() + st::msgNameFont->height; + } + if (const HistoryReply *reply = toHistoryReply(parent)) { + _height += st::msgReplyPadding.top() + st::msgReplyBarSize.height() + st::msgReplyPadding.bottom(); + } else if (const HistoryForwarded *fwd = toHistoryForwarded(parent)) { + if (parent->out() || !parent->history()->peer->chat) { + _height += st::msgPadding.top(); + } + _height += st::msgServiceNameFont->height; + } + if (!_caption.isEmpty()) { + int32 textw = w - st::mediaPadding.left() - st::mediaPadding.right(); + if (!parent->out()) { // substract Download / Save As button + textw -= st::mediaSaveDelta + _buttonWidth; + } + _height += st::webPagePhotoSkip + _caption.countHeight(textw); + } + return _height; +} + ImagePtr HistoryVideo::replyPreview() { if (data->replyPreview->isNull() && !data->thumb->isNull()) { if (data->thumb->loaded()) { @@ -2309,11 +2369,11 @@ void HistoryAudio::draw(QPainter &p, const HistoryItem *parent, bool selected, i if (hovered && !pressed && textlnkDown()) hovered = false; int32 btnw = _buttonWidth, btnh = st::mediaSaveButton.height, btnx = width - _buttonWidth, btny = skipy + (_height - skipy - btnh) / 2; - p.fillRect(QRect(btnx, btny, btnw, btnh), (selected ? st::msgInSelectBG : (hovered ? st::mediaSaveButton.overBgColor : st::mediaSaveButton.bgColor))->b); - - style::color shadow(selected ? (out ? st::msgOutSelectShadow : st::msgInSelectShadow) : (out ? st::msgOutShadow : st::msgInShadow)); - p.fillRect(btnx, btny + btnh, btnw, st::msgShadow, shadow->b); - + style::color bg(selected ? st::msgInSelectBg : (hovered ? st::mediaSaveButton.overBgColor : st::mediaSaveButton.bgColor)); + style::color sh(selected ? st::msgInSelectShadow : st::msgInShadow); + RoundCorners cors(selected ? MessageInSelectedCorners : (hovered ? ButtonHoverCorners : MessageInCorners)); + App::roundRect(p, btnx, btny, btnw, btnh, bg, cors, &sh); + p.setPen((hovered ? st::mediaSaveButton.overColor : st::mediaSaveButton.color)->p); p.setFont(st::mediaSaveButton.font->f); QString btnText(lang(data->loader ? lng_media_cancel : (already ? lng_media_open_with : lng_media_download))); @@ -2322,11 +2382,10 @@ void HistoryAudio::draw(QPainter &p, const HistoryItem *parent, bool selected, i width -= btnw + st::mediaSaveDelta; } - style::color bg(selected ? (out ? st::msgOutSelectBG : st::msgInSelectBG) : (out ? st::msgOutBG : st::msgInBG)); - p.fillRect(QRect(0, 0, width, _height), bg->b); - - style::color shadow(selected ? (out ? st::msgOutSelectShadow : st::msgInSelectShadow) : (out ? st::msgOutShadow : st::msgInShadow)); - p.fillRect(0, _height, width, st::msgShadow, shadow->b); + style::color bg(selected ? (out ? st::msgOutSelectBg : st::msgInSelectBg) : (out ? st::msgOutBg : st::msgInBg)); + style::color sh(selected ? (out ? st::msgOutSelectShadow : st::msgInSelectShadow) : (out ? st::msgOutShadow : st::msgInShadow)); + RoundCorners cors(selected ? (out ? MessageOutSelectedCorners : MessageInSelectedCorners) : (out ? MessageOutCorners : MessageInCorners)); + App::roundRect(p, 0, 0, width, _height, bg, cors, &sh); if (!parent->out() && parent->history()->peer->chat) { p.setFont(st::msgNameFont->f); @@ -2354,7 +2413,7 @@ void HistoryAudio::draw(QPainter &p, const HistoryItem *parent, bool selected, i } p.drawPixmap(QPoint(st::mediaPadding.left(), skipy + st::mediaPadding.top()), App::sprite(), img); if (selected) { - p.fillRect(st::mediaPadding.left(), skipy + st::mediaPadding.top(), st::mediaThumbSize, st::mediaThumbSize, textstyleCurrent()->selectOverlay->b); + App::roundRect(p, st::mediaPadding.left(), skipy + st::mediaPadding.top(), st::mediaThumbSize, st::mediaThumbSize, textstyleCurrent()->selectOverlay, SelectedOverlayCorners); } int32 tleft = st::mediaPadding.left() + st::mediaThumbSize + st::mediaPadding.right(); @@ -2639,10 +2698,10 @@ void HistoryDocument::draw(QPainter &p, const HistoryItem *parent, bool selected if (hovered && !pressed && textlnkDown()) hovered = false; int32 btnw = _buttonWidth, btnh = st::mediaSaveButton.height, btnx = width - _buttonWidth, btny = skipy + (_height - skipy - btnh) / 2; - p.fillRect(QRect(btnx, btny, btnw, btnh), (selected ? st::msgInSelectBG : (hovered ? st::mediaSaveButton.overBgColor : st::mediaSaveButton.bgColor))->b); - - style::color shadow(selected ? (out ? st::msgOutSelectShadow : st::msgInSelectShadow) : (out ? st::msgOutShadow : st::msgInShadow)); - p.fillRect(btnx, btny + btnh, btnw, st::msgShadow, shadow->b); + style::color bg(selected ? st::msgInSelectBg : (hovered ? st::mediaSaveButton.overBgColor : st::mediaSaveButton.bgColor)); + style::color sh(selected ? st::msgInSelectShadow : st::msgInShadow); + RoundCorners cors(selected ? MessageInSelectedCorners : (hovered ? ButtonHoverCorners : MessageInCorners)); + App::roundRect(p, btnx, btny, btnw, btnh, bg, cors, &sh); p.setPen((hovered ? st::mediaSaveButton.overColor : st::mediaSaveButton.color)->p); p.setFont(st::mediaSaveButton.font->f); @@ -2652,11 +2711,10 @@ void HistoryDocument::draw(QPainter &p, const HistoryItem *parent, bool selected width -= btnw + st::mediaSaveDelta; } - style::color bg(selected ? (out ? st::msgOutSelectBG : st::msgInSelectBG) : (out ? st::msgOutBG : st::msgInBG)); - p.fillRect(QRect(0, 0, width, _height), bg->b); - - style::color shadow(selected ? (out ? st::msgOutSelectShadow : st::msgInSelectShadow) : (out ? st::msgOutShadow : st::msgInShadow)); - p.fillRect(0, _height, width, st::msgShadow, shadow->b); + style::color bg(selected ? (out ? st::msgOutSelectBg : st::msgInSelectBg) : (out ? st::msgOutBg : st::msgInBg)); + style::color sh(selected ? (out ? st::msgOutSelectShadow : st::msgInSelectShadow) : (out ? st::msgOutShadow : st::msgInShadow)); + RoundCorners cors(selected ? (out ? MessageOutSelectedCorners : MessageInSelectedCorners) : (out ? MessageOutCorners : MessageInCorners)); + App::roundRect(p, 0, 0, width, _height, bg, cors, &sh); if (!parent->out() && parent->history()->peer->chat) { p.setFont(st::msgNameFont->f); @@ -2669,13 +2727,12 @@ void HistoryDocument::draw(QPainter &p, const HistoryItem *parent, bool selected fwd->drawForwardedFrom(p, st::mediaPadding.left(), fwdFrom, width - st::mediaPadding.left() - st::mediaPadding.right(), selected); } if (_thumbw) { - int32 rf(cIntRetinaFactor()); - p.drawPixmap(QPoint(st::mediaPadding.left(), skipy + st::mediaPadding.top()), data->thumb->pix(_thumbw), QRect(_thumbx * rf, _thumby * rf, st::mediaThumbSize * rf, st::mediaThumbSize * rf)); + p.drawPixmap(QPoint(st::mediaPadding.left(), skipy + st::mediaPadding.top()), data->thumb->pixSingle(_thumbw, 0, st::mediaThumbSize, st::mediaThumbSize)); } else { p.drawPixmap(QPoint(st::mediaPadding.left(), skipy + st::mediaPadding.top()), App::sprite(), (out ? st::mediaDocOutImg : st::mediaDocInImg)); } if (selected) { - p.fillRect(st::mediaPadding.left(), skipy + st::mediaPadding.top(), st::mediaThumbSize, st::mediaThumbSize, textstyleCurrent()->selectOverlay->b); + App::roundRect(p, st::mediaPadding.left(), skipy + st::mediaPadding.top(), st::mediaThumbSize, st::mediaThumbSize, textstyleCurrent()->selectOverlay, SelectedOverlayCorners); } int32 tleft = st::mediaPadding.left() + st::mediaThumbSize + st::mediaPadding.right(); @@ -2980,10 +3037,8 @@ void HistorySticker::draw(QPainter &p, const HistoryItem *parent, bool selected, int32 dateW = usex + usew - dateX - st::msgDateImgDelta; int32 dateH = _height - dateY - st::msgDateImgDelta; - p.fillRect(dateX, dateY, dateW, dateH, st::msgDateImgBg->b); - if (selected) { - p.fillRect(dateX, dateY, dateW, dateH, textstyleCurrent()->selectOverlay->b); - } + App::roundRect(p, dateX, dateY, dateW, dateH, selected ? st::msgDateImgSelectBg : st::msgDateImgBg, selected ? DateSelectedCorners : DateCorners); + p.setFont(st::msgDateFont->f); p.setPen(st::msgDateImgColor->p); p.drawText(dateX + st::msgDateImgPadding.x(), dateY + st::msgDateImgPadding.y() + st::msgDateFont->ascent, time); @@ -3006,14 +3061,7 @@ void HistorySticker::draw(QPainter &p, const HistoryItem *parent, bool selected, int32 rw = width - usew, rh = st::msgReplyPadding.top() + st::msgReplyBarSize.height() + st::msgReplyPadding.bottom(); int32 rx = parent->out() ? 0 : usew, ry = _height - rh; - p.setPen(Qt::NoPen); - QRect r(rx, ry, rw, rh); - p.setBrush(App::msgServiceBG()->b); - p.drawRoundedRect(r, st::msgServiceRadius, st::msgServiceRadius); - if (selected) { - p.setBrush(textstyleCurrent()->selectOverlay->b); - p.drawRoundedRect(r, st::msgServiceRadius, st::msgServiceRadius); - } + App::roundRect(p, rx, ry, rw, rh, App::msgServiceBg(), selected ? ServiceSelectedCorners : ServiceCorners, &App::msgServiceSelectBg()); reply->drawReplyTo(p, rx + st::msgReplyPadding.left(), ry, rw - st::msgReplyPadding.left() - st::msgReplyPadding.right(), selected, true); } @@ -3217,11 +3265,10 @@ void HistoryContact::draw(QPainter &p, const HistoryItem *parent, bool selected, width = _maxw; } - style::color bg(selected ? (out ? st::msgOutSelectBG : st::msgInSelectBG) : (out ? st::msgOutBG : st::msgInBG)); - p.fillRect(QRect(0, 0, width, _height), bg->b); - - style::color shadow(selected ? (out ? st::msgOutSelectShadow : st::msgInSelectShadow) : (out ? st::msgOutShadow : st::msgInShadow)); - p.fillRect(0, _height, width, st::msgShadow, shadow->b); + style::color bg(selected ? (out ? st::msgOutSelectBg : st::msgInSelectBg) : (out ? st::msgOutBg : st::msgInBg)); + style::color sh(selected ? (out ? st::msgOutSelectShadow : st::msgInSelectShadow) : (out ? st::msgOutShadow : st::msgInShadow)); + RoundCorners cors(selected ? (out ? MessageOutSelectedCorners : MessageInSelectedCorners) : (out ? MessageOutCorners : MessageInCorners)); + App::roundRect(p, 0, 0, width, _height, bg, cors, &sh); if (!parent->out() && parent->history()->peer->chat) { p.setFont(st::msgNameFont->f); @@ -3234,7 +3281,10 @@ void HistoryContact::draw(QPainter &p, const HistoryItem *parent, bool selected, fwd->drawForwardedFrom(p, st::mediaPadding.left(), fwdFrom, width - st::mediaPadding.left() - st::mediaPadding.right(), selected); } - p.drawPixmap(st::mediaPadding.left(), skipy + st::mediaPadding.top(), (contact ? contact->photo : userDefPhoto(1))->pix(st::mediaThumbSize)); + p.drawPixmap(st::mediaPadding.left(), skipy + st::mediaPadding.top(), (contact ? contact->photo : userDefPhoto(1))->pixRounded(st::mediaThumbSize)); + if (selected) { + App::roundRect(p, st::mediaPadding.left(), skipy + st::mediaPadding.top(), st::mediaThumbSize, st::mediaThumbSize, textstyleCurrent()->selectOverlay, SelectedOverlayCorners); + } int32 tleft = st::mediaPadding.left() + st::mediaThumbSize + st::mediaPadding.right(); int32 twidth = width - tleft - st::mediaPadding.right(); @@ -3307,9 +3357,10 @@ HistoryWebPage::HistoryWebPage(WebPageData *data) : HistoryMedia() void HistoryWebPage::initDimensions(const HistoryItem *parent) { if (data->pendingTill) { - _maxw = st::webPageLeft + st::linkFont->m.width(lang((data->pendingTill < 0) ? lng_attach_failed : lng_profile_loading)); - _minh = st::replyHeight; - _height = _minh; + _maxw = _minh = _height = 0; + //_maxw = st::webPageLeft + st::linkFont->m.width(lang((data->pendingTill < 0) ? lng_attach_failed : lng_profile_loading)); + //_minh = st::replyHeight; + //_height = _minh; return; } if (!_openl && !data->url.isEmpty()) _openl = TextLinkPtr(new TextLink(data->url)); @@ -3404,29 +3455,29 @@ void HistoryWebPage::initDimensions(const HistoryItem *parent) { void HistoryWebPage::draw(QPainter &p, const HistoryItem *parent, bool selected, int32 width) const { if (width < 0) width = w; - if (width < 1) return; + if (width < 1 || data->pendingTill) return; int32 bottomSkip = 0; - if (!data->pendingTill) { + //if (!data->pendingTill) { if (data->photo) { bottomSkip += st::webPagePhotoSkip; if (_asArticle || (st::webPageLeft + qMax(_pixw, int16(st::minPhotoSize)) + parent->timeWidth(true) > width)) { bottomSkip += (st::msgDateFont->height - st::msgDateDelta.y()); } } - } + //} style::color bar = (selected ? (parent->out() ? st::msgOutReplyBarSelColor : st::msgInReplyBarSelColor) : (parent->out() ? st::msgOutReplyBarColor : st::msgInReplyBarColor)); style::color semibold = (selected ? (parent->out() ? st::msgOutServiceSelColor : st::msgInServiceSelColor) : (parent->out() ? st::msgOutServiceColor : st::msgInServiceColor)); style::color regular = (selected ? (parent->out() ? st::msgOutSelectDateColor : st::msgInSelectDateColor) : (parent->out() ? st::msgOutDateColor : st::msgInDateColor)); p.fillRect(0, 0, st::webPageBar, _height - bottomSkip, bar->b); - if (data->pendingTill) { - p.setFont(st::linkFont->f); - p.setPen(regular->p); - p.drawText(st::webPageLeft, (_minh - st::linkFont->height) / 2 + st::linkFont->ascent, lang(data->pendingTill < 0 ? lng_attach_failed : lng_profile_loading)); - return; - } + //if (data->pendingTill) { + // p.setFont(st::linkFont->f); + // p.setPen(regular->p); + // p.drawText(st::webPageLeft, (_minh - st::linkFont->height) / 2 + st::linkFont->ascent, lang(data->pendingTill < 0 ? lng_attach_failed : lng_profile_loading)); + // return; + //} p.save(); p.translate(st::webPageLeft, 0); @@ -3440,19 +3491,13 @@ void HistoryWebPage::draw(QPainter &p, const HistoryItem *parent, bool selected, bool full = data->photo->medium->loaded(); QPixmap pix; if (full) { - pix = data->photo->medium->pixSingle(_pixw, _pixh); + pix = data->photo->medium->pixSingle(_pixw, _pixh, pixwidth, pixheight); } else { - pix = data->photo->thumb->pixBlurredSingle(_pixw, _pixh); + pix = data->photo->thumb->pixBlurredSingle(_pixw, _pixh, pixwidth, pixheight); } - if (_pixw < pixwidth || _pixh < pixheight) { - p.fillRect(QRect(width - pixwidth, 0, pixwidth, pixheight), st::black->b); - } - if (_pixw > pixwidth) { - p.drawPixmap(QRect(width - pixwidth, (pixheight - _pixh) / 2, pixwidth, _pixh), pix, QRect(cIntRetinaFactor() * (_pixw - pixwidth) / 2, 0, cIntRetinaFactor() * pixwidth, cIntRetinaFactor() * _pixh)); - } else if (_pixh > pixheight) { - p.drawPixmap(QRect(width - pixwidth + (pixwidth - _pixw) / 2, 0, _pixw, pixheight), pix, QRect(0, cIntRetinaFactor() * (_pixh - pixheight) / 2, cIntRetinaFactor() * _pixw, cIntRetinaFactor() * pixheight)); - } else { - p.drawPixmap(QPoint(width - pixwidth + (pixwidth - _pixw) / 2, (pixheight - _pixh) / 2), pix); + p.drawPixmap(width - pixwidth, 0, pix); + if (selected) { + App::roundRect(p, width - pixwidth, 0, pixwidth, pixheight, textstyleCurrent()->selectOverlay, SelectedOverlayCorners); } } int32 articleLines = 5; @@ -3511,14 +3556,11 @@ void HistoryWebPage::draw(QPainter &p, const HistoryItem *parent, bool selected, bool full = data->photo->full->loaded(); QPixmap pix; if (full) { - pix = data->photo->full->pixSingle(_pixw, _pixh); + pix = data->photo->full->pixSingle(_pixw, _pixh, pixwidth, pixheight); } else { - pix = data->photo->thumb->pixBlurredSingle(_pixw, _pixh); + pix = data->photo->thumb->pixBlurredSingle(_pixw, _pixh, pixwidth, pixheight); } - if (_pixw < pixwidth || _pixh < pixheight) { - p.fillRect(QRect(0, 0, pixwidth, pixheight), st::black->b); - } - p.drawPixmap(QPoint((pixwidth - _pixw) / 2, (pixheight - _pixh) / 2), pix); + p.drawPixmap(0, 0, pix); if (!full) { uint64 dt = itemAnimations().animate(parent, getms()); int32 cnt = int32(st::photoLoaderCnt), period = int32(st::photoLoaderPeriod), t = dt % period, delta = int32(st::photoLoaderDelta); @@ -3541,7 +3583,7 @@ void HistoryWebPage::draw(QPainter &p, const HistoryItem *parent, bool selected, } if (selected) { - p.fillRect(0, 0, pixwidth, pixheight, textstyleCurrent()->selectOverlay->b); + App::roundRect(p, 0, 0, pixwidth, pixheight, textstyleCurrent()->selectOverlay, SelectedOverlayCorners); } if (data->type == WebPageVideo) { @@ -3556,10 +3598,8 @@ void HistoryWebPage::draw(QPainter &p, const HistoryItem *parent, bool selected, int32 dateW = pixwidth - dateX - st::msgDateImgDelta; int32 dateH = pixheight - dateY - st::msgDateImgDelta; - p.fillRect(dateX, dateY, dateW, dateH, st::msgDateImgBg->b); - if (selected) { - p.fillRect(dateX, dateY, dateW, dateH, textstyleCurrent()->selectOverlay->b); - } + App::roundRect(p, dateX, dateY, dateW, dateH, selected ? st::msgDateImgSelectBg : st::msgDateImgBg, selected ? DateSelectedCorners : DateCorners); + p.setFont(st::msgDateFont->f); p.setPen(st::msgDateImgColor->p); p.drawText(dateX + st::msgDateImgPadding.x(), dateY + st::msgDateImgPadding.y() + st::msgDateFont->ascent, _duration); @@ -4214,13 +4254,18 @@ void HistoryImageLink::draw(QPainter &p, const HistoryItem *parent, bool selecte int skipx = 0, skipy = 0, height = _height; const HistoryReply *reply = toHistoryReply(parent); const HistoryForwarded *fwd = toHistoryForwarded(parent); + bool out = parent->out(); + if (reply || !_title.isEmpty() || !_description.isEmpty()) { skipx = st::mediaPadding.left(); - style::color bg(selected ? (parent->out() ? st::msgOutSelectBG : st::msgInSelectBG) : (parent->out() ? st::msgOutBG : st::msgInBG)); - p.fillRect(QRect(0, 0, width, _height), bg->b); + style::color bg(selected ? (out ? st::msgOutSelectBg : st::msgInSelectBg) : (out ? st::msgOutBg : st::msgInBg)); + style::color sh(selected ? (out ? st::msgOutSelectShadow : st::msgInSelectShadow) : (out ? st::msgOutShadow : st::msgInShadow)); + RoundCorners cors(selected ? (out ? MessageOutSelectedCorners : MessageInSelectedCorners) : (out ? MessageOutCorners : MessageInCorners)); + App::roundRect(p, 0, 0, width, _height, bg, cors, &sh); + int replyFrom = 0, fwdFrom = 0; - if (!parent->out() && parent->history()->peer->chat) { + if (!out && parent->history()->peer->chat) { replyFrom = st::msgPadding.top() + st::msgNameFont->height; fwdFrom = st::msgPadding.top() + st::msgNameFont->height; skipy += replyFrom; @@ -4255,27 +4300,37 @@ void HistoryImageLink::draw(QPainter &p, const HistoryItem *parent, bool selecte skipy += st::webPagePhotoSkip; } height -= skipy + st::mediaPadding.bottom(); + } else { + QPixmap **cors = App::corners(selected ? InSelectedShadowCorners : InShadowCorners); + int32 cw = cors[0]->width() / cIntRetinaFactor(), ch = cors[0]->height(); + style::color shadow(selected ? st::msgInSelectShadow : st::msgInShadow); + p.fillRect(cw, _height, width - 2 * cw, st::msgShadow, shadow->b); + p.fillRect(0, _height - ch, cw, st::msgShadow, shadow->b); + p.fillRect(width - cw, _height - ch, cw, st::msgShadow, shadow->b); + p.drawPixmap(0, _height - ch + st::msgShadow, *cors[2]); + p.drawPixmap(width - cw, _height - ch + st::msgShadow, *cors[3]); } data->load(); - bool out = parent->out(); QPixmap toDraw; if (data && !data->thumb->isNull()) { int32 w = data->thumb->width(), h = data->thumb->height(); + QPixmap pix; if (width * h == height * w || (w == convertScale(fullWidth()) && h == convertScale(fullHeight()))) { - p.drawPixmap(QPoint(skipx, skipy), data->thumb->pixSingle(width, height)); + pix = data->thumb->pixSingle(width, height, width, height); } else { p.fillRect(QRect(skipx, skipy, width, height), st::black->b); if (width * h > height * w) { int32 nw = height * w / h; - p.drawPixmap(QPoint(skipx + (width - nw) / 2, skipy), data->thumb->pixSingle(nw, height)); + pix = data->thumb->pixSingle(nw, height, width, height); } else { int32 nh = width * h / w; - p.drawPixmap(QPoint(skipx, skipy + (height - nh) / 2), data->thumb->pixSingle(width, nh)); + pix = data->thumb->pixSingle(width, nh, width, height); } } + p.drawPixmap(QPoint(skipx, skipy), pix); } else { - p.fillRect(QRect(skipx, skipy, width, height), st::black->b); + App::roundRect(p, skipx, skipy, width, height, st::black, BlackCorners); } if (data) { switch (data->type) { @@ -4298,10 +4353,8 @@ void HistoryImageLink::draw(QPainter &p, const HistoryItem *parent, bool selecte } } if (selected) { - p.fillRect(skipx, skipy, width, height, textstyleCurrent()->selectOverlay->b); + App::roundRect(p, skipx, skipy, width, height, textstyleCurrent()->selectOverlay, SelectedOverlayCorners); } - style::color shadow(selected ? st::msgInSelectShadow : st::msgInShadow); - p.fillRect(0, _height, width + (skipx ? (st::mediaPadding.left() + st::mediaPadding.right()) : 0), st::msgShadow, shadow->b); // date QString time(parent->time()); @@ -4314,10 +4367,8 @@ void HistoryImageLink::draw(QPainter &p, const HistoryItem *parent, bool selecte int32 dateW = skipx + width - dateX - st::msgDateImgDelta; int32 dateH = skipy + height - dateY - st::msgDateImgDelta; - p.fillRect(dateX, dateY, dateW, dateH, st::msgDateImgBg->b); - if (selected) { - p.fillRect(dateX, dateY, dateW, dateH, textstyleCurrent()->selectOverlay->b); - } + App::roundRect(p, dateX, dateY, dateW, dateH, selected ? st::msgDateImgSelectBg : st::msgDateImgBg, selected ? DateSelectedCorners : DateCorners); + p.setFont(st::msgDateFont->f); p.setPen(st::msgDateImgColor->p); p.drawText(dateX + st::msgDateImgPadding.x(), dateY + st::msgDateImgPadding.y() + st::msgDateFont->ascent, time); @@ -4616,7 +4667,7 @@ void HistoryMessage::initMediaFromDocument(DocumentData *doc) { void HistoryMessage::initDimensions(const QString &text) { if (!_media || !text.isEmpty()) { // !justMedia() - if (_media) { + if (_media && _media->isDisplayed()) { _text.setText(st::msgFont, text, _historyTextOptions); } else { _text.setText(st::msgFont, text + textcmdSkipBlock(timeWidth(true), st::msgDateFont->height - st::msgDateDelta.y()), _historyTextOptions); @@ -4635,9 +4686,26 @@ void HistoryMessage::initDimensions(const HistoryItem *parent) { _maxw += st::msgPadding.left() + st::msgPadding.right(); if (_media) { _media->initDimensions(this); - int32 maxw = _media->maxWidth() + st::msgPadding.left() + st::msgPadding.right(); - if (maxw > _maxw) _maxw = maxw; - _minh += st::msgPadding.bottom() + _media->minHeight(); + if (_media->isDisplayed() && _text.hasSkipBlock()) { + QString was = HistoryMessage::selectedText(FullItemSel); + if (!was.isEmpty()) { + _text.setText(st::msgFont, was, _historyTextOptions); // without date skip + _textWidth = 0; + _textHeight = 0; + } + } else if (!_media->isDisplayed() && !_text.hasSkipBlock()) { + QString was = HistoryMessage::selectedText(FullItemSel); + if (!was.isEmpty()) { + _text.setText(st::msgFont, was + textcmdSkipBlock(timeWidth(true), st::msgDateFont->height - st::msgDateDelta.y()), _historyTextOptions); // without date skip + _textWidth = 0; + _textHeight = 0; + } + } + if (_media->isDisplayed()) { + int32 maxw = _media->maxWidth() + st::msgPadding.left() + st::msgPadding.right(); + if (maxw > _maxw) _maxw = maxw; + _minh += st::msgPadding.bottom() + _media->minHeight(); + } } } fromNameUpdated(); @@ -4674,21 +4742,28 @@ HistoryMedia *HistoryMessage::getMedia(bool inOverview) const { void HistoryMessage::setMedia(const MTPmessageMedia &media) { if ((!_media || _media->isImageLink()) && media.type() == mtpc_messageMediaEmpty) return; - bool wasMedia = false; + bool mediaWasDisplayed = false; if (_media) { - wasMedia = true; + mediaWasDisplayed = _media->isDisplayed(); delete _media; _media = 0; } QString t; initMedia(media, t); - if (_media && !wasMedia) { + if (_media && _media->isDisplayed() && !mediaWasDisplayed) { QString was = HistoryMessage::selectedText(FullItemSel); if (!was.isEmpty()) { _text.setText(st::msgFont, was, _historyTextOptions); // without date skip _textWidth = 0; _textHeight = 0; } + } else if (mediaWasDisplayed && (!_media || !_media->isDisplayed())) { + QString was = HistoryMessage::selectedText(FullItemSel); + if (!was.isEmpty()) { + _text.setText(st::msgFont, was + textcmdSkipBlock(timeWidth(true), st::msgDateFont->height - st::msgDateDelta.y()), _historyTextOptions); // without date skip + _textWidth = 0; + _textHeight = 0; + } } initDimensions(0); if (App::main()) App::main()->itemResized(this); @@ -4728,7 +4803,7 @@ void HistoryMessage::draw(QPainter &p, uint32 selection) const { } if (!out() && _history->peer->chat) { - p.drawPixmap(left, _height - st::msgMargin.bottom() - st::msgPhotoSize, _from->photo->pix(st::msgPhotoSize)); + p.drawPixmap(left, _height - st::msgMargin.bottom() - st::msgPhotoSize, _from->photo->pixRounded(st::msgPhotoSize)); // width -= st::msgPhotoSkip; left += st::msgPhotoSkip; } @@ -4746,11 +4821,10 @@ void HistoryMessage::draw(QPainter &p, uint32 selection) const { } else { QRect r(left, st::msgMargin.top(), width, _height - st::msgMargin.top() - st::msgMargin.bottom()); - style::color bg(selected ? (out() ? st::msgOutSelectBG : st::msgInSelectBG) : (out() ? st::msgOutBG : st::msgInBG)); - p.fillRect(r, bg->b); - - style::color shadow(selected ? (out() ? st::msgOutSelectShadow : st::msgInSelectShadow) : (out() ? st::msgOutShadow : st::msgInShadow)); - p.fillRect(left, _height - st::msgMargin.bottom(), width, st::msgShadow, shadow->b); + style::color bg(selected ? (out() ? st::msgOutSelectBg : st::msgInSelectBg) : (out() ? st::msgOutBg : st::msgInBg)); + style::color sh(selected ? (out() ? st::msgOutSelectShadow : st::msgInSelectShadow) : (out() ? st::msgOutShadow : st::msgInShadow)); + RoundCorners cors(selected ? (out() ? MessageOutSelectedCorners : MessageInSelectedCorners) : (out() ? MessageOutCorners : MessageInCorners)); + App::roundRect(p, r, bg, cors, &sh); if (!out() && _history->peer->chat) { p.setFont(st::msgNameFont->f); @@ -4822,7 +4896,7 @@ int32 HistoryMessage::resize(int32 width, bool dontRecountText, const HistoryIte if (_media) _media->resize(_maxw - st::msgPadding.left() - st::msgPadding.right(), dontRecountText, this); } else { _height = _textHeight; - if (_media) _height += st::msgPadding.bottom() + _media->resize(nwidth, dontRecountText, this); + if (_media && _media->isDisplayed()) _height += st::msgPadding.bottom() + _media->resize(nwidth, dontRecountText, this); } if (!out() && _history->peer->chat) { _height += st::msgNameFont->height; @@ -4906,7 +4980,7 @@ void HistoryMessage::getState(TextLinkPtr &lnk, bool &inText, int32 x, int32 y) void HistoryMessage::getStateFromMessageText(TextLinkPtr &lnk, bool &inText, int32 x, int32 y, const QRect &r) const { QRect trect(r.marginsAdded(-st::msgPadding)); TextLinkPtr medialnk; - if (_media) { + if (_media && _media->isDisplayed()) { if (y >= trect.bottom() - _media->height() && y < trect.bottom()) { _media->getState(lnk, inText, x - trect.left(), y + _media->height() - trect.bottom(), this); return; @@ -4943,7 +5017,7 @@ void HistoryMessage::getSymbol(uint16 &symbol, bool &after, bool &upon, int32 x, r.setTop(r.top() + st::msgNameFont->height); } QRect trect(r.marginsAdded(-st::msgPadding)); - if (_media) { + if (_media && _media->isDisplayed()) { trect.setBottom(trect.bottom() - _media->height() - st::msgPadding.bottom()); } _text.getSymbol(symbol, after, upon, x - trect.x(), y - trect.y(), trect.width()); @@ -5323,14 +5397,9 @@ void HistoryReply::drawReplyTo(QPainter &p, int32 x, int32 y, int32 w, bool sele ImagePtr replyPreview = replyToMsg->getMedia()->replyPreview(); if (!replyPreview->isNull()) { QRect to(x + st::msgReplyBarSkip, y + st::msgReplyPadding.top() + st::msgReplyBarPos.y(), st::msgReplyBarSize.height(), st::msgReplyBarSize.height()); - if (replyPreview->width() == replyPreview->height()) { - p.drawPixmap(to.x(), to.y(), replyPreview->pix()); - } else { - QRect from = (replyPreview->width() > replyPreview->height()) ? QRect((replyPreview->width() - replyPreview->height()) / 2, 0, replyPreview->height(), replyPreview->height()) : QRect(0, (replyPreview->height() - replyPreview->width()) / 2, replyPreview->width(), replyPreview->width()); - p.drawPixmap(to, replyPreview->pix(), from); - } + p.drawPixmap(to.x(), to.y(), replyPreview->pixSingle(replyPreview->width() / cIntRetinaFactor(), replyPreview->height() / cIntRetinaFactor(), to.width(), to.height())); if (selected) { - p.fillRect(to, textstyleCurrent()->selectOverlay->b); + App::roundRect(p, to, textstyleCurrent()->selectOverlay, SelectedOverlayCorners); } } } @@ -5653,16 +5722,8 @@ void HistoryServiceMsg::draw(QPainter &p, uint32 selection) const { left += (width - _maxw) / 2; width = _maxw; } -// QRect r(0, st::msgServiceMargin.top(), _history->width, height); - QRect r(left, st::msgServiceMargin.top(), width, height); - p.setBrush(App::msgServiceBG()->b); - p.setPen(Qt::NoPen); -// p.fillRect(r, App::msgServiceBG()->b); - p.drawRoundedRect(r, st::msgServiceRadius, st::msgServiceRadius); - if (selection == FullItemSel) { - p.setBrush(st::msgServiceSelectBG->b); - p.drawRoundedRect(r, st::msgServiceRadius, st::msgServiceRadius); - } + App::roundRect(p, left, st::msgServiceMargin.top(), width, height, App::msgServiceBg(), (selection == FullItemSel) ? ServiceSelectedCorners : ServiceCorners); + p.setBrush(Qt::NoBrush); p.setPen(st::msgServiceColor->p); p.setFont(st::msgServiceFont->f); diff --git a/Telegram/SourceFiles/history.h b/Telegram/SourceFiles/history.h index e82d93c47..f708d1951 100644 --- a/Telegram/SourceFiles/history.h +++ b/Telegram/SourceFiles/history.h @@ -805,6 +805,9 @@ public: virtual const QString inDialogsText() const = 0; virtual const QString inHistoryText() const = 0; virtual bool hasPoint(int32 x, int32 y, const HistoryItem *parent, int32 width = -1) const = 0; + virtual bool isDisplayed() const { + return true; + } virtual int32 countHeight(const HistoryItem *parent, int32 width = -1) const { return height(); } @@ -873,6 +876,7 @@ public: } const QString inDialogsText() const; const QString inHistoryText() const; + const Text &captionForClone() const; bool hasPoint(int32 x, int32 y, const HistoryItem *parent, int32 width = -1) const; void getState(TextLinkPtr &lnk, bool &inText, int32 x, int32 y, const HistoryItem *parent, int32 width = -1) const; HistoryMedia *clone() const; @@ -914,6 +918,7 @@ public: void initDimensions(const HistoryItem *parent); void draw(QPainter &p, const HistoryItem *parent, bool selected, int32 width = -1) const; + int32 resize(int32 width, bool dontRecountText = false, const HistoryItem *parent = 0); HistoryMediaType type() const { return MediaTypeVideo; } @@ -1097,6 +1102,9 @@ public: void initDimensions(const HistoryItem *parent); void draw(QPainter &p, const HistoryItem *parent, bool selected, int32 width = -1) const; + bool isDisplayed() const { + return !data->pendingTill; + } int32 resize(int32 width, bool dontRecountText = false, const HistoryItem *parent = 0); HistoryMediaType type() const { return MediaTypeWebPage; @@ -1252,7 +1260,9 @@ public: QString notificationText() const; void updateMedia(const MTPMessageMedia &media) { - if (_media) _media->updateFrom(media); + if (_media) { + _media->updateFrom(media); + } } void updateStickerEmoji(); diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp index 06026c920..6e47154ae 100644 --- a/Telegram/SourceFiles/historywidget.cpp +++ b/Telegram/SourceFiles/historywidget.cpp @@ -1424,10 +1424,8 @@ void HistoryHider::paintEvent(QPaintEvent *e) { p.setPen(st::black->p); toText.drawElided(p, box.left() + (box.width() - toTextWidth) / 2, box.top() + st::boxPadding.top(), toTextWidth + 1); } else { - p.setBrush(st::forwardBG->b); - p.setPen(Qt::NoPen); int32 w = st::forwardMargins.left() + _chooseWidth + st::forwardMargins.right(), h = st::forwardMargins.top() + st::forwardFont->height + st::forwardMargins.bottom(); - p.drawRoundedRect((width() - w) / 2, (height() - h) / 2, w, h, st::forwardRadius, st::forwardRadius); + App::roundRect(p, (width() - w) / 2, (height() - h) / 2, w, h, st::forwardBg, ForwardCorners); p.setPen(st::white->p); p.drawText(box, lang(lng_forward_choose), QTextOption(style::al_center)); @@ -4096,12 +4094,7 @@ void HistoryWidget::drawFieldBackground(QPainter &p) { ImagePtr replyPreview = _replyTo->getMedia()->replyPreview(); if (!replyPreview->isNull()) { QRect to(replyLeft, backy + st::msgReplyPadding.top(), st::msgReplyBarSize.height(), st::msgReplyBarSize.height()); - if (replyPreview->width() == replyPreview->height()) { - p.drawPixmap(to.x(), to.y(), replyPreview->pix()); - } else { - QRect from = (replyPreview->width() > replyPreview->height()) ? QRect((replyPreview->width() - replyPreview->height()) / 2, 0, replyPreview->height(), replyPreview->height()) : QRect(0, (replyPreview->height() - replyPreview->width()) / 2, replyPreview->width(), replyPreview->width()); - p.drawPixmap(to, replyPreview->pix(), from); - } + p.drawPixmap(to.x(), to.y(), replyPreview->pixSingle(replyPreview->width() / cIntRetinaFactor(), replyPreview->height() / cIntRetinaFactor(), to.width(), to.height())); } replyLeft += st::msgReplyBarSize.height() + st::msgReplyBarSkip - st::msgReplyBarSize.width() - st::msgReplyBarPos.x(); } @@ -4230,9 +4223,7 @@ void HistoryWidget::paintEvent(QPaintEvent *e) { style::font font(st::msgServiceFont); int32 w = font->m.width(lang(lng_willbe_history)) + st::msgPadding.left() + st::msgPadding.right(), h = font->height + st::msgServicePadding.top() + st::msgServicePadding.bottom() + 2; QRect tr((width() - w) / 2, (height() - _field.height() - 2 * st::sendPadding - h) / 2, w, h); - p.setPen(Qt::NoPen); - p.setBrush(App::msgServiceBG()->b); - p.drawRoundedRect(tr, st::msgServiceRadius, st::msgServiceRadius); + App::roundRect(p, tr, App::msgServiceBg(), ServiceCorners); p.setPen(st::msgServiceColor->p); p.setFont(font->f); diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index e558a09b4..cc776c9ac 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -1013,6 +1013,7 @@ void MainWidget::stopAnimActive() { } void MainWidget::searchMessages(const QString &query) { + App::wnd()->hideMediaview(); dialogs.searchMessages(query); if (!cWideMode()) onShowDialogs(); } @@ -2507,6 +2508,8 @@ void MainWidget::openLocalUrl(const QString &url) { } void MainWidget::openUserByName(const QString &username, bool toProfile) { + App::wnd()->hideMediaview(); + UserData *user = App::userByName(username); if (user) { if (toProfile) { @@ -2520,10 +2523,12 @@ void MainWidget::openUserByName(const QString &username, bool toProfile) { } void MainWidget::joinGroupByHash(const QString &hash) { + App::wnd()->hideMediaview(); MTP::send(MTPmessages_CheckChatInvite(MTP_string(hash)), rpcDone(&MainWidget::inviteCheckDone, hash), rpcFail(&MainWidget::inviteCheckFail)); } void MainWidget::stickersBox(const MTPInputStickerSet &set) { + App::wnd()->hideMediaview(); StickerSetBox *box = new StickerSetBox(set); connect(box, SIGNAL(installed(uint64)), this, SLOT(onStickersInstalled(uint64))); App::wnd()->showLayer(box); diff --git a/Telegram/SourceFiles/mediaview.cpp b/Telegram/SourceFiles/mediaview.cpp index fe45aed96..af8b8f98e 100644 --- a/Telegram/SourceFiles/mediaview.cpp +++ b/Telegram/SourceFiles/mediaview.cpp @@ -41,6 +41,14 @@ namespace { MediaView *_view; }; + + + TextParseOptions _captionTextOptions = { + TextParseLinks | TextParseMentions | TextParseHashtags | TextParseMultiline | TextParseRichText, // flags + 0, // maxw + 0, // maxh + Qt::LayoutDirectionAuto, // dir + }; } MediaView::MediaView() : TWidget(App::wnd()), @@ -282,6 +290,14 @@ void MediaView::updateControls() { } else { _leftNavVisible = _rightNavVisible = false; } + if (!_caption.isEmpty()) { + int32 skipw = qMax(_dateNav.left() + _dateNav.width(), _headerNav.left() + _headerNav.width()); + int32 maxw = qMin(qMax(width() - 2 * skipw - st::mvCaptionPadding.left() - st::mvCaptionPadding.right() - 2 * st::mvCaptionMargin.width(), int(st::msgMinWidth)), _caption.maxWidth()); + int32 maxh = qMin(_caption.countHeight(maxw), int(height() / 4 - st::mvCaptionPadding.top() - st::mvCaptionPadding.bottom() - 2 * st::mvCaptionMargin.height())); + _captionRect = QRect((width() - maxw) / 2, height() - maxh - st::mvCaptionPadding.bottom() - st::mvCaptionMargin.height(), maxw, maxh); + } else { + _captionRect = QRect(); + } updateOver(mapFromGlobal(QCursor::pos())); update(); } @@ -336,7 +352,7 @@ bool MediaView::animStep(float64 msp) { } else { a_cOpacity.update(dt, anim::linear); } - QRegion toUpdate = QRegion() + (_over == OverLeftNav ? _leftNav : _leftNavIcon) + (_over == OverRightNav ? _rightNav : _rightNavIcon) + (_over == OverClose ? _closeNav : _closeNavIcon) + _saveNavIcon + _moreNavIcon + _headerNav + _nameNav + _dateNav; + QRegion toUpdate = QRegion() + (_over == OverLeftNav ? _leftNav : _leftNavIcon) + (_over == OverRightNav ? _rightNav : _rightNavIcon) + (_over == OverClose ? _closeNav : _closeNavIcon) + _saveNavIcon + _moreNavIcon + _headerNav + _nameNav + _dateNav + _captionRect.marginsAdded(st::mvCaptionPadding); update(toUpdate); if (dt < 1) result = true; } @@ -641,7 +657,7 @@ void MediaView::showPhoto(PhotoData *photo, HistoryItem *context) { findCurrent(); } - displayPhoto(photo); + displayPhoto(photo, context); preloadData(0); activateControls(); } @@ -679,7 +695,7 @@ void MediaView::showPhoto(PhotoData *photo, PeerData *context) { loadBack(); } } - displayPhoto(photo); + displayPhoto(photo, 0); preloadData(0); activateControls(); } @@ -722,11 +738,18 @@ void MediaView::showDocument(DocumentData *doc, HistoryItem *context) { activateControls(); } -void MediaView::displayPhoto(PhotoData *photo) { +void MediaView::displayPhoto(PhotoData *photo, HistoryItem *item) { _photo = photo; _doc = 0; _zoom = 0; + _caption = Text(); + if (HistoryMessage *itemMsg = item ? item->toHistoryMessage() : 0) { + if (HistoryPhoto *photoMsg = dynamic_cast(itemMsg->getMedia())) { + _caption.setText(st::mvCaptionFont, photoMsg->captionForClone().original(0, 0xFFFF), _captionTextOptions); + } + } + _zoomToScreen = 0; MTP::clearLoaderPriorities(); _full = -1; @@ -774,6 +797,7 @@ void MediaView::displayPhoto(PhotoData *photo) { void MediaView::displayDocument(DocumentData *doc, HistoryItem *item) { _doc = doc; + _caption = Text(); QString already = _doc->already(true); if (_doc->sticker && !_doc->sticker->img->isNull() && _doc->sticker->img->loaded()) { _currentGif.stop(); @@ -966,12 +990,12 @@ void MediaView::paintEvent(QPaintEvent *e) { _full = 1; } else if (_full < 0 && _photo->medium->loaded()) { int32 h = int((_photo->full->height() * (qreal(w) / qreal(_photo->full->width()))) + 0.9999); - _current = _photo->medium->pixBlurredNoCache(w, h); + _current = _photo->medium->pixNoCache(w, h, true, true); if (cRetina()) _current.setDevicePixelRatio(cRetinaFactor()); _full = 0; } else if (_current.isNull() && _photo->thumb->loaded()) { int32 h = int((_photo->full->height() * (qreal(w) / qreal(_photo->full->width()))) + 0.9999); - _current = _photo->thumb->pixBlurredNoCache(w, h); + _current = _photo->thumb->pixNoCache(w, h, true, true); if (cRetina()) _current.setDevicePixelRatio(cRetinaFactor()); } } @@ -1028,9 +1052,7 @@ void MediaView::paintEvent(QPaintEvent *e) { _saveMsgOpacity.update(qMin(progress, 1.), anim::linear); if (_saveMsgOpacity.current() > 0) { p.setOpacity(_saveMsgOpacity.current()); - p.setBrush(st::medviewSaveMsg->b); - p.setPen(Qt::NoPen); - p.drawRoundedRect(_saveMsg, st::medviewSaveMsgRadius, st::medviewSaveMsgRadius); + App::roundRect(p, _saveMsg, st::medviewSaveMsg, MediaviewSaveCorners); p.drawPixmap(_saveMsg.topLeft() + st::medviewSaveMsgCheckPos, App::sprite(), st::medviewSaveMsgCheck); p.setPen(st::white->p); @@ -1216,6 +1238,23 @@ void MediaView::paintEvent(QPaintEvent *e) { p.drawLine(_dateNav.left(), _dateNav.top() + st::mvFont->ascent + 1, _dateNav.right(), _dateNav.top() + st::mvFont->ascent + 1); } } + + // caption + if (!_caption.isEmpty()) { + QRect outer(_captionRect.marginsAdded(st::mvCaptionPadding)); + if (outer.intersects(r)) { + p.setOpacity(co); + p.setBrush(st::mvCaptionBg->b); + p.setPen(Qt::NoPen); + p.drawRoundedRect(outer, st::mvCaptionRadius, st::mvCaptionRadius); + if (_captionRect.intersects(r)) { + textstyleSet(&st::medviewSaveAsTextStyle); + p.setPen(st::white->p); + _caption.drawElided(p, _captionRect.x(), _captionRect.y(), _captionRect.width(), _captionRect.height() / st::mvCaptionFont->height); + textstyleRestore(); + } + } + } } // static uint64 t = getms(); @@ -1334,7 +1373,7 @@ void MediaView::moveToNext(int32 delta) { if (HistoryItem *item = App::histItemById(_history->_overview[_overview][_index])) { _msgid = item->id; switch (item->getMedia()->type()) { - case MediaTypePhoto: displayPhoto(static_cast(item->getMedia())->photo()); preloadData(delta); break; + case MediaTypePhoto: displayPhoto(static_cast(item->getMedia())->photo(), item); preloadData(delta); break; case MediaTypeDocument: displayDocument(static_cast(item->getMedia())->document(), item); preloadData(delta); break; case MediaTypeSticker: displayDocument(static_cast(item->getMedia())->document(), item); preloadData(delta); break; } @@ -1346,7 +1385,7 @@ void MediaView::moveToNext(int32 delta) { } else if (_user) { if (newIndex >= 0 && newIndex < _user->photos.size()) { _index = newIndex; - displayPhoto(_user->photos[_index]); + displayPhoto(_user->photos[_index], 0); preloadData(delta); } if (delta > 0 && _index > _user->photos.size() - MediaOverviewStartPerPage) { @@ -1405,7 +1444,10 @@ void MediaView::preloadData(int32 delta) { void MediaView::mousePressEvent(QMouseEvent *e) { updateOver(e->pos()); if (_menu || !_receiveMouse) return; - textlnkDown(textlnkOver()); + + if (textlnkDown() != textlnkOver()) { + textlnkDown(textlnkOver()); + } if (e->button() == Qt::LeftButton) { _down = OverNone; @@ -1540,9 +1582,12 @@ bool MediaView::updateOverState(OverState newState) { void MediaView::updateOver(QPoint pos) { TextLinkPtr lnk; bool inText; - if (_saveMsgStarted) { + + if (_saveMsgStarted && _saveMsg.contains(pos)) { _saveMsgText.getState(lnk, inText, pos.x() - _saveMsg.x() - st::medviewSaveMsgPadding.left(), pos.y() - _saveMsg.y() - st::medviewSaveMsgPadding.top(), _saveMsg.width() - st::medviewSaveMsgPadding.left() - st::medviewSaveMsgPadding.right()); - } + } else if (_captionRect.contains(pos)) { + _caption.getState(lnk, inText, pos.x() - _captionRect.x(), pos.y() - _captionRect.y(), _captionRect.width()); + } // retina if (pos.x() == width()) { @@ -1555,7 +1600,7 @@ void MediaView::updateOver(QPoint pos) { if (lnk != textlnkOver()) { textlnkOver(lnk); setCursor((textlnkOver() || textlnkDown()) ? style::cur_pointer : style::cur_default); - updateImage(); + update(QRegion(_saveMsg) + _captionRect); } if (_pressed || _dragging) return; diff --git a/Telegram/SourceFiles/mediaview.h b/Telegram/SourceFiles/mediaview.h index 700aca0fe..77a0a738d 100644 --- a/Telegram/SourceFiles/mediaview.h +++ b/Telegram/SourceFiles/mediaview.h @@ -99,7 +99,7 @@ public slots: private: - void displayPhoto(PhotoData *photo); + void displayPhoto(PhotoData *photo, HistoryItem *item); void displayDocument(DocumentData *doc, HistoryItem *item); void findCurrent(); void loadBack(); @@ -124,6 +124,9 @@ private: QString _dateText; QString _headerText; + Text _caption; + QRect _captionRect; + uint64 _animStarted; int32 _width, _x, _y, _w, _h, _xStart, _yStart; diff --git a/Telegram/SourceFiles/overviewwidget.cpp b/Telegram/SourceFiles/overviewwidget.cpp index 4d17ccba6..720041606 100644 --- a/Telegram/SourceFiles/overviewwidget.cpp +++ b/Telegram/SourceFiles/overviewwidget.cpp @@ -688,7 +688,7 @@ void OverviewInner::paintEvent(QPaintEvent *e) { bool out = item->out(); int32 mw = media->maxWidth(), left = (out ? st::msgMargin.right() : st::msgMargin.left()) + (out && mw < w ? (w - mw) : 0); if (!out && _hist->peer->chat) { - p.drawPixmap(left, media->countHeight(item, w) - st::msgPhotoSize, item->from()->photo->pix(st::msgPhotoSize)); + p.drawPixmap(left, media->countHeight(item, w) - st::msgPhotoSize, item->from()->photo->pixRounded(st::msgPhotoSize)); left += st::msgPhotoSkip; } @@ -720,9 +720,7 @@ void OverviewInner::paintEvent(QPaintEvent *e) { width = strwidth; QRect r(left, st::msgServiceMargin.top(), width, height); - p.setBrush(App::msgServiceBG()->b); - p.setPen(Qt::NoPen); - p.drawRoundedRect(r, st::msgServiceRadius, st::msgServiceRadius); + App::roundRect(p, r, App::msgServiceBg(), ServiceCorners); p.setBrush(Qt::NoBrush); p.setPen(st::msgServiceColor->p); diff --git a/Telegram/SourceFiles/window.cpp b/Telegram/SourceFiles/window.cpp index b3358ab12..0b506c4ca 100644 --- a/Telegram/SourceFiles/window.cpp +++ b/Telegram/SourceFiles/window.cpp @@ -810,9 +810,7 @@ void Window::hideLayer(bool fast) { layerBG = 0; } } - if (_mediaView && !_mediaView->isHidden()) { - _mediaView->hide(); - } + hideMediaview(); } bool Window::hideInnerLayer() { @@ -843,10 +841,14 @@ void Window::layerHidden() { layerBG->deleteLater(); } layerBG = 0; - if (_mediaView && !_mediaView->isHidden()) _mediaView->hide(); + hideMediaview(); setInnerFocus(); } +void Window::hideMediaview() { + if (_mediaView && !_mediaView->isHidden()) _mediaView->hide(); +} + void Window::setInnerFocus() { if (_passcode) { _passcode->setInnerFocus(); @@ -1691,7 +1693,7 @@ QImage Window::iconWithCounter(int size, int count, style::color bg, bool smallI void Window::sendPaths() { if (App::passcoded()) return; - if (_mediaView && !_mediaView->isHidden()) _mediaView->hide(); + hideMediaview(); if (settings) { hideSettings(); } else { diff --git a/Telegram/SourceFiles/window.h b/Telegram/SourceFiles/window.h index 61504ec17..3835f573c 100644 --- a/Telegram/SourceFiles/window.h +++ b/Telegram/SourceFiles/window.h @@ -231,6 +231,7 @@ public: void changingMsgId(HistoryItem *row, MsgId newId); bool isActive(bool cached = true) const; + void hideMediaview(); public slots: