diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index 5a0a439a9..d6fdb2a4d 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -21,9 +21,6 @@ add_subdirectory(lib_qr) add_subdirectory(lib_webrtc) add_subdirectory(codegen) -include(lib_ui/cmake/generate_styles.cmake) -include(cmake/generate_numbers.cmake) - get_filename_component(src_loc SourceFiles REALPATH) get_filename_component(res_loc Resources REALPATH) @@ -37,33 +34,6 @@ include(cmake/td_lang.cmake) include(cmake/td_scheme.cmake) include(cmake/td_ui.cmake) -set(style_files - boxes/boxes.style - calls/calls.style - chat_helpers/chat_helpers.style - export/view/export.style - info/info.style - intro/intro.style - media/view/media_view.style - media/player/media_player.style - overview/overview.style - passport/passport.style - profile/profile.style - settings/settings.style - ui/filter_icons.style -) - -set(dependent_style_files - ${submodules_loc}/lib_ui/ui/colors.palette - ${submodules_loc}/lib_ui/ui/basic.style - ${submodules_loc}/lib_ui/ui/layers/layers.style - ${submodules_loc}/lib_ui/ui/widgets/widgets.style - ${src_loc}/ui/td_common.style -) - -generate_styles(Telegram ${src_loc} "${style_files}" "${dependent_style_files}") -generate_numbers(Telegram ${res_loc}/numbers.txt) - set_target_properties(Telegram PROPERTIES AUTOMOC ON AUTORCC ON) target_link_libraries(Telegram @@ -359,8 +329,6 @@ PRIVATE core/launcher.h core/local_url_handlers.cpp core/local_url_handlers.h - core/mime_type.cpp - core/mime_type.h core/sandbox.cpp core/sandbox.h core/shortcuts.cpp @@ -1023,8 +991,6 @@ PRIVATE ui/filter_icons.h ui/filter_icon_panel.cpp ui/filter_icon_panel.h - ui/grouped_layout.cpp - ui/grouped_layout.h ui/item_text_options.cpp ui/item_text_options.h ui/resize_area.h diff --git a/Telegram/SourceFiles/app.cpp b/Telegram/SourceFiles/app.cpp index 4be5be873..8c244d6fa 100644 --- a/Telegram/SourceFiles/app.cpp +++ b/Telegram/SourceFiles/app.cpp @@ -22,6 +22,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/view/history_view_service_message.h" #include "media/audio/media_audio.h" #include "ui/image/image.h" +#include "ui/cached_round_corners.h" #include "inline_bots/inline_bot_layout_item.h" #include "core/crash_reports.h" #include "core/update_checker.h" @@ -66,14 +67,6 @@ HistoryView::Element *hoveredItem = nullptr, *pressedLinkItem = nullptr, *mousedItem = nullptr; -struct CornersPixmaps { - QPixmap p[4]; -}; -QVector corners; -using CornersMap = QMap; -CornersMap cornersMap; -QImage cornersMaskLarge[4], cornersMaskSmall[4]; - } // namespace namespace App { @@ -104,114 +97,12 @@ namespace App { return result; } - void prepareCorners(RoundCorners index, int32 radius, const QBrush &brush, const style::color *shadow = nullptr, QImage *cors = nullptr) { - Expects(::corners.size() > index); - - int32 r = radius * cIntRetinaFactor(), s = st::msgShadow * cIntRetinaFactor(); - QImage rect(r * 3, r * 3 + (shadow ? s : 0), QImage::Format_ARGB32_Premultiplied), localCors[4]; - { - Painter p(&rect); - PainterHighQualityEnabler hq(p); - - p.setCompositionMode(QPainter::CompositionMode_Source); - p.fillRect(QRect(0, 0, rect.width(), rect.height()), Qt::transparent); - p.setCompositionMode(QPainter::CompositionMode_SourceOver); - p.setPen(Qt::NoPen); - if (shadow) { - p.setBrush((*shadow)->b); - p.drawRoundedRect(0, s, r * 3, r * 3, r, r); - } - p.setBrush(brush); - 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)); - if (index != SmallMaskCorners && index != LargeMaskCorners) { - for (int i = 0; i < 4; ++i) { - ::corners[index].p[i] = pixmapFromImageInPlace(std::move(cors[i])); - ::corners[index].p[i].setDevicePixelRatio(cRetinaFactor()); - } - } - } - - void createMaskCorners() { - QImage mask[4]; - prepareCorners(SmallMaskCorners, st::buttonRadius, QColor(255, 255, 255), nullptr, mask); - for (int i = 0; i < 4; ++i) { - ::cornersMaskSmall[i] = mask[i].convertToFormat(QImage::Format_ARGB32_Premultiplied); - ::cornersMaskSmall[i].setDevicePixelRatio(cRetinaFactor()); - } - prepareCorners(LargeMaskCorners, st::historyMessageRadius, QColor(255, 255, 255), nullptr, mask); - for (int i = 0; i < 4; ++i) { - ::cornersMaskLarge[i] = mask[i].convertToFormat(QImage::Format_ARGB32_Premultiplied); - ::cornersMaskLarge[i].setDevicePixelRatio(cRetinaFactor()); - } - } - - void createPaletteCorners() { - prepareCorners(MenuCorners, st::buttonRadius, st::menuBg); - prepareCorners(BoxCorners, st::boxRadius, st::boxBg); - prepareCorners(BotKbOverCorners, st::dateRadius, st::msgBotKbOverBgAdd); - prepareCorners(StickerCorners, st::dateRadius, st::msgServiceBg); - prepareCorners(StickerSelectedCorners, st::dateRadius, st::msgServiceBgSelected); - prepareCorners(SelectedOverlaySmallCorners, st::buttonRadius, st::msgSelectOverlay); - prepareCorners(SelectedOverlayLargeCorners, st::historyMessageRadius, st::msgSelectOverlay); - prepareCorners(DateCorners, st::dateRadius, st::msgDateImgBg); - prepareCorners(DateSelectedCorners, st::dateRadius, st::msgDateImgBgSelected); - prepareCorners(OverviewVideoCorners, st::overviewVideoStatusRadius, st::msgDateImgBg); - prepareCorners(OverviewVideoSelectedCorners, st::overviewVideoStatusRadius, st::msgDateImgBgSelected); - prepareCorners(InShadowCorners, st::historyMessageRadius, st::msgInShadow); - prepareCorners(InSelectedShadowCorners, st::historyMessageRadius, st::msgInShadowSelected); - prepareCorners(ForwardCorners, st::historyMessageRadius, st::historyForwardChooseBg); - prepareCorners(MediaviewSaveCorners, st::mediaviewControllerRadius, st::mediaviewSaveMsgBg); - prepareCorners(EmojiHoverCorners, st::buttonRadius, st::emojiPanHover); - prepareCorners(StickerHoverCorners, st::buttonRadius, st::emojiPanHover); - prepareCorners(BotKeyboardCorners, st::buttonRadius, st::botKbBg); - prepareCorners(PhotoSelectOverlayCorners, st::buttonRadius, st::overviewPhotoSelectOverlay); - - prepareCorners(Doc1Corners, st::buttonRadius, st::msgFile1Bg); - prepareCorners(Doc2Corners, st::buttonRadius, st::msgFile2Bg); - prepareCorners(Doc3Corners, st::buttonRadius, st::msgFile3Bg); - prepareCorners(Doc4Corners, st::buttonRadius, st::msgFile4Bg); - - prepareCorners(MessageInCorners, st::historyMessageRadius, st::msgInBg, &st::msgInShadow); - prepareCorners(MessageInSelectedCorners, st::historyMessageRadius, st::msgInBgSelected, &st::msgInShadowSelected); - prepareCorners(MessageOutCorners, st::historyMessageRadius, st::msgOutBg, &st::msgOutShadow); - prepareCorners(MessageOutSelectedCorners, st::historyMessageRadius, st::msgOutBgSelected, &st::msgOutShadowSelected); - - prepareCorners(SendFilesBoxAlbumGroupCorners, st::sendBoxAlbumGroupRadius, st::callFingerprintBg); - } - - void createCorners() { - ::corners.resize(RoundCornersCount); - createMaskCorners(); - createPaletteCorners(); - } - - void clearCorners() { - ::corners.clear(); - ::cornersMap.clear(); - } - void initMedia() { - createCorners(); + Ui::StartCachedCorners(); using Update = Window::Theme::BackgroundUpdate; static auto subscription = Window::Theme::Background()->add_subscription([](const Update &update) { if (update.paletteChanged()) { - createPaletteCorners(); - - if (const auto m = App::main()) { // multi good - m->updateScrollColors(); - } - HistoryView::serviceColorsUpdated(); - } else if (update.type == Update::Type::New) { - prepareCorners(StickerCorners, st::dateRadius, st::msgServiceBg); - prepareCorners(StickerSelectedCorners, st::dateRadius, st::msgServiceBgSelected); - if (const auto m = App::main()) { // multi good m->updateScrollColors(); } @@ -221,8 +112,7 @@ namespace App { } void deinitMedia() { - clearCorners(); - + Ui::FinishCachedCorners(); Data::clearGlobalStructures(); } @@ -396,132 +286,4 @@ namespace App { return QPixmap::fromImage(std::move(image), Qt::ColorOnly); } - void rectWithCorners(Painter &p, QRect rect, const style::color &bg, RoundCorners index, RectParts corners) { - auto parts = RectPart::Top - | RectPart::NoTopBottom - | RectPart::Bottom - | corners; - roundRect(p, rect, bg, index, nullptr, parts); - if ((corners & RectPart::AllCorners) != RectPart::AllCorners) { - const auto size = ::corners[index].p[0].width() / cIntRetinaFactor(); - if (!(corners & RectPart::TopLeft)) { - p.fillRect(rect.x(), rect.y(), size, size, bg); - } - if (!(corners & RectPart::TopRight)) { - p.fillRect(rect.x() + rect.width() - size, rect.y(), size, size, bg); - } - if (!(corners & RectPart::BottomLeft)) { - p.fillRect(rect.x(), rect.y() + rect.height() - size, size, size, bg); - } - if (!(corners & RectPart::BottomRight)) { - p.fillRect(rect.x() + rect.width() - size, rect.y() + rect.height() - size, size, size, bg); - } - } - } - - void complexOverlayRect(Painter &p, QRect rect, ImageRoundRadius radius, RectParts corners) { - if (radius == ImageRoundRadius::Ellipse) { - PainterHighQualityEnabler hq(p); - p.setPen(Qt::NoPen); - p.setBrush(p.textPalette().selectOverlay); - p.drawEllipse(rect); - } else { - auto overlayCorners = (radius == ImageRoundRadius::Small) - ? SelectedOverlaySmallCorners - : SelectedOverlayLargeCorners; - const auto bg = p.textPalette().selectOverlay; - rectWithCorners(p, rect, bg, overlayCorners, corners); - } - } - - void complexLocationRect(Painter &p, QRect rect, ImageRoundRadius radius, RectParts corners) { - rectWithCorners(p, rect, st::msgInBg, MessageInCorners, corners); - } - - void roundRect(Painter &p, int32 x, int32 y, int32 w, int32 h, style::color bg, const CornersPixmaps &corner, const style::color *shadow, RectParts parts) { - auto cornerWidth = corner.p[0].width() / cIntRetinaFactor(); - auto cornerHeight = corner.p[0].height() / cIntRetinaFactor(); - if (w < 2 * cornerWidth || h < 2 * cornerHeight) return; - if (w > 2 * cornerWidth) { - if (parts & RectPart::Top) { - p.fillRect(x + cornerWidth, y, w - 2 * cornerWidth, cornerHeight, bg); - } - if (parts & RectPart::Bottom) { - p.fillRect(x + cornerWidth, y + h - cornerHeight, w - 2 * cornerWidth, cornerHeight, bg); - if (shadow) { - p.fillRect(x + cornerWidth, y + h, w - 2 * cornerWidth, st::msgShadow, *shadow); - } - } - } - if (h > 2 * cornerHeight) { - if ((parts & RectPart::NoTopBottom) == RectPart::NoTopBottom) { - p.fillRect(x, y + cornerHeight, w, h - 2 * cornerHeight, bg); - } else { - if (parts & RectPart::Left) { - p.fillRect(x, y + cornerHeight, cornerWidth, h - 2 * cornerHeight, bg); - } - if ((parts & RectPart::Center) && w > 2 * cornerWidth) { - p.fillRect(x + cornerWidth, y + cornerHeight, w - 2 * cornerWidth, h - 2 * cornerHeight, bg); - } - if (parts & RectPart::Right) { - p.fillRect(x + w - cornerWidth, y + cornerHeight, cornerWidth, h - 2 * cornerHeight, bg); - } - } - } - if (parts & RectPart::TopLeft) { - p.drawPixmap(x, y, corner.p[0]); - } - if (parts & RectPart::TopRight) { - p.drawPixmap(x + w - cornerWidth, y, corner.p[1]); - } - if (parts & RectPart::BottomLeft) { - p.drawPixmap(x, y + h - cornerHeight, corner.p[2]); - } - if (parts & RectPart::BottomRight) { - p.drawPixmap(x + w - cornerWidth, y + h - cornerHeight, corner.p[3]); - } - } - - void roundRect(Painter &p, int32 x, int32 y, int32 w, int32 h, style::color bg, RoundCorners index, const style::color *shadow, RectParts parts) { - roundRect(p, x, y, w, h, bg, ::corners[index], shadow, parts); - } - - void roundShadow(Painter &p, int32 x, int32 y, int32 w, int32 h, style::color shadow, RoundCorners index, RectParts parts) { - auto &corner = ::corners[index]; - auto cornerWidth = corner.p[0].width() / cIntRetinaFactor(); - auto cornerHeight = corner.p[0].height() / cIntRetinaFactor(); - if (parts & RectPart::Bottom) { - p.fillRect(x + cornerWidth, y + h, w - 2 * cornerWidth, st::msgShadow, shadow); - } - if (parts & RectPart::BottomLeft) { - p.fillRect(x, y + h - cornerHeight, cornerWidth, st::msgShadow, shadow); - p.drawPixmap(x, y + h - cornerHeight + st::msgShadow, corner.p[2]); - } - if (parts & RectPart::BottomRight) { - p.fillRect(x + w - cornerWidth, y + h - cornerHeight, cornerWidth, st::msgShadow, shadow); - p.drawPixmap(x + w - cornerWidth, y + h - cornerHeight + st::msgShadow, corner.p[3]); - } - } - - void roundRect(Painter &p, int32 x, int32 y, int32 w, int32 h, style::color bg, ImageRoundRadius radius, RectParts parts) { - auto colorKey = ((uint32(bg->c.alpha()) & 0xFF) << 24) | ((uint32(bg->c.red()) & 0xFF) << 16) | ((uint32(bg->c.green()) & 0xFF) << 8) | ((uint32(bg->c.blue()) & 0xFF) << 24); - auto i = cornersMap.find(colorKey); - if (i == cornersMap.cend()) { - QImage images[4]; - switch (radius) { - case ImageRoundRadius::Small: prepareCorners(SmallMaskCorners, st::buttonRadius, bg, nullptr, images); break; - case ImageRoundRadius::Large: prepareCorners(LargeMaskCorners, st::historyMessageRadius, bg, nullptr, images); break; - default: p.fillRect(x, y, w, h, bg); return; - } - - CornersPixmaps pixmaps; - for (int j = 0; j < 4; ++j) { - pixmaps.p[j] = pixmapFromImageInPlace(std::move(images[j])); - pixmaps.p[j].setDevicePixelRatio(cRetinaFactor()); - } - i = cornersMap.insert(colorKey, pixmaps); - } - roundRect(p, x, y, w, h, bg, i.value(), nullptr, parts); - } - } diff --git a/Telegram/SourceFiles/app.h b/Telegram/SourceFiles/app.h index fc7c3c6a8..5d12e4665 100644 --- a/Telegram/SourceFiles/app.h +++ b/Telegram/SourceFiles/app.h @@ -8,54 +8,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #pragma once #include "data/data_types.h" -#include "ui/rect_part.h" - -enum class ImageRoundRadius; namespace HistoryView { class Element; } // namespace HistoryView -enum RoundCorners : int { - SmallMaskCorners = 0x00, // for images - LargeMaskCorners, - - BoxCorners, - MenuCorners, - BotKbOverCorners, - StickerCorners, - StickerSelectedCorners, - SelectedOverlaySmallCorners, - SelectedOverlayLargeCorners, - DateCorners, - DateSelectedCorners, - OverviewVideoCorners, - OverviewVideoSelectedCorners, - ForwardCorners, - MediaviewSaveCorners, - EmojiHoverCorners, - StickerHoverCorners, - BotKeyboardCorners, - PhotoSelectOverlayCorners, - - Doc1Corners, - Doc2Corners, - Doc3Corners, - Doc4Corners, - - InShadowCorners, // for photos without bg - InSelectedShadowCorners, - - MessageInCorners, // with shadow - MessageInSelectedCorners, - MessageOutCorners, - MessageOutSelectedCorners, - - SendFilesBoxAlbumGroupCorners, - - RoundCornersCount -}; - namespace App { QString formatPhone(QString phone); @@ -90,20 +47,4 @@ namespace App { QImage readImage(const QString &file, QByteArray *format = nullptr, bool opaque = true, bool *animated = nullptr, QByteArray *content = 0); QPixmap pixmapFromImageInPlace(QImage &&image); - void complexOverlayRect(Painter &p, QRect rect, ImageRoundRadius radius, RectParts corners); - void complexLocationRect(Painter &p, QRect rect, ImageRoundRadius radius, RectParts corners); - - void roundRect(Painter &p, int32 x, int32 y, int32 w, int32 h, style::color bg, RoundCorners index, const style::color *shadow = nullptr, RectParts parts = RectPart::Full); - inline void roundRect(Painter &p, const QRect &rect, style::color bg, RoundCorners index, const style::color *shadow = nullptr, RectParts parts = RectPart::Full) { - return roundRect(p, rect.x(), rect.y(), rect.width(), rect.height(), bg, index, shadow, parts); - } - void roundShadow(Painter &p, int32 x, int32 y, int32 w, int32 h, style::color shadow, RoundCorners index, RectParts parts = RectPart::Full); - inline void roundShadow(Painter &p, const QRect &rect, style::color shadow, RoundCorners index, RectParts parts = RectPart::Full) { - return roundShadow(p, rect.x(), rect.y(), rect.width(), rect.height(), shadow, index, parts); - } - void roundRect(Painter &p, int32 x, int32 y, int32 w, int32 h, style::color bg, ImageRoundRadius radius, RectParts parts = RectPart::Full); - inline void roundRect(Painter &p, const QRect &rect, style::color bg, ImageRoundRadius radius, RectParts parts = RectPart::Full) { - return roundRect(p, rect.x(), rect.y(), rect.width(), rect.height(), bg, radius, parts); - } - }; diff --git a/Telegram/SourceFiles/boxes/edit_caption_box.cpp b/Telegram/SourceFiles/boxes/edit_caption_box.cpp index 371d966f5..e3f6e530f 100644 --- a/Telegram/SourceFiles/boxes/edit_caption_box.cpp +++ b/Telegram/SourceFiles/boxes/edit_caption_box.cpp @@ -50,11 +50,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/text/text_options.h" #include "ui/chat/attach/attach_prepare.h" #include "ui/controls/emoji_button.h" +#include "ui/cached_round_corners.h" #include "window/window_session_controller.h" #include "confirm_box.h" #include "apiwrap.h" +#include "app.h" // App::pixmapFromImageInPlace. #include "facades.h" // App::LambdaDelayed. -#include "app.h" #include "styles/style_layers.h" #include "styles/style_boxes.h" #include "styles/style_chat_helpers.h" @@ -484,7 +485,7 @@ void EditCaptionBox::updateEditPreview() { const auto fileinfo = QFileInfo(file->path); const auto filename = fileinfo.fileName(); - _isImage = fileIsImage(filename, file->mime); + _isImage = Core::FileIsImage(filename, file->mime); _isAudio = false; _animated = false; _photo = false; @@ -516,7 +517,7 @@ void EditCaptionBox::updateEditPreview() { if (shouldAsDoc) { auto nameString = filename; if (const auto song = std::get_if(fileMedia)) { - nameString = DocumentData::ComposeNameString( + nameString = Ui::ComposeNameString( filename, song->title, song->performer); @@ -623,9 +624,10 @@ void EditCaptionBox::createEditMediaButton() { _editMedia.create(this, st::editMediaButton); updateEditMediaButton(); _editMedia->setClickedCallback( - App::LambdaDelayed(st::historyAttach.ripple.hideDuration, this, [=] { - buttonCallback(); - })); + App::LambdaDelayed( + st::historyAttach.ripple.hideDuration, + this, + buttonCallback)); } void EditCaptionBox::prepare() { @@ -894,7 +896,7 @@ void EditCaptionBox::paintEvent(QPaintEvent *e) { const auto namewidth = w - nameleft - editButton; const auto x = (width() - w) / 2, y = st::boxPhotoPadding.top(); -// App::roundRect(p, x, y, w, h, st::msgInBg, MessageInCorners, &st::msgInShadow); +// Ui::FillRoundCorner(p, x, y, w, h, st::msgInBg, Ui::MessageInCorners, &st::msgInShadow); if (_thumbw) { QRect rthumb(style::rtlrect(x + 0, y + 0, st::msgFileThumbSize, st::msgFileThumbSize, width())); diff --git a/Telegram/SourceFiles/boxes/send_files_box.cpp b/Telegram/SourceFiles/boxes/send_files_box.cpp index b927cc1f4..f6ac6012b 100644 --- a/Telegram/SourceFiles/boxes/send_files_box.cpp +++ b/Telegram/SourceFiles/boxes/send_files_box.cpp @@ -32,6 +32,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/scroll_area.h" #include "ui/wrap/fade_wrap.h" #include "ui/chat/attach/attach_prepare.h" +#include "ui/chat/attach/attach_album_preview.h" +#include "ui/chat/attach/attach_single_file_preview.h" #include "ui/text/format_values.h" #include "ui/grouped_layout.h" #include "ui/text/text_options.h" @@ -55,14 +57,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace { constexpr auto kMinPreviewWidth = 20; -constexpr auto kShrinkDuration = crl::time(150); -constexpr auto kDragDuration = crl::time(200); -enum class ButtonType { - Edit, - Delete, - None, -}; +using Ui::SendFilesWay; inline bool CanAddUrls(const QList &urls) { return !urls.isEmpty() && ranges::all_of(urls, &QUrl::isLocalFile); @@ -78,58 +74,6 @@ inline bool IsSingleItem(const Ui::PreparedList &list) { return list.files.size() == 1; } -QRect PaintAlbumThumbButtons( - Painter &p, - QPoint point, - int outerWidth, - float64 shrinkProgress) { - - const auto skipInternal = st::sendBoxAlbumGroupEditInternalSkip; - const auto size = st::sendBoxAlbumGroupHeight; - const auto skipRight = st::sendBoxAlbumGroupSkipRight; - const auto skipTop = st::sendBoxAlbumGroupSkipTop; - const auto groupWidth = size * 2 + skipInternal; - - // If the width is tiny, it would be better to not display the buttons. - if (groupWidth > outerWidth) { - return QRect(); - } - - // If the width is too small, - // it would be better to display the buttons in the center. - const auto groupX = point.x() + ((groupWidth + skipRight * 2 > outerWidth) - ? (outerWidth - groupWidth) / 2 - : outerWidth - skipRight - groupWidth); - const auto groupY = point.y() + skipTop; - const auto deleteLeft = skipInternal + size; - - p.setOpacity(1.0 - shrinkProgress); - - QRect groupRect(groupX, groupY, groupWidth, size); - App::roundRect( - p, - groupRect, - st::callFingerprintBg, - SendFilesBoxAlbumGroupCorners); - - const auto editP = st::sendBoxAlbumGroupEditButtonIconPosition; - const auto deleteP = st::sendBoxAlbumGroupDeleteButtonIconPosition; - - st::sendBoxAlbumGroupEditButtonIcon.paintInCenter( - p, - QRect(groupX + editP.x(), groupY + editP.y(), size, size)); - st::sendBoxAlbumGroupDeleteButtonIcon.paintInCenter( - p, - QRect( - groupX + deleteLeft + deleteP.x(), - groupY + deleteP.y(), - size, - size)); - p.setOpacity(1); - - return groupRect; -} - void FileDialogCallback( FileDialog::OpenResult &&result, bool isAlbum, @@ -195,571 +139,6 @@ private: }; -class SingleFilePreview : public Ui::RpWidget { -public: - SingleFilePreview( - QWidget *parent, - const Ui::PreparedFile &file); - - rpl::producer desiredHeightValue() const override; - -protected: - void paintEvent(QPaintEvent *e) override; - -private: - void preparePreview(const Ui::PreparedFile &file); - void prepareThumb(const QImage &preview); - - QPixmap _fileThumb; - Ui::Text::String _nameText; - bool _fileIsAudio = false; - bool _fileIsImage = false; - QString _statusText; - int _statusWidth = 0; - -}; - -class AlbumThumb { -public: - AlbumThumb( - const Ui::PreparedFile &file, - const Ui::GroupMediaLayout &layout, - QWidget *parent, - Fn editCallback, - Fn deleteCallback); - - void moveToLayout(const Ui::GroupMediaLayout &layout); - void animateLayoutToInitial(); - void resetLayoutAnimation(); - - int photoHeight() const; - - void paintInAlbum( - Painter &p, - int left, - int top, - float64 shrinkProgress, - float64 moveProgress); - void paintPhoto(Painter &p, int left, int top, int outerWidth); - void paintFile(Painter &p, int left, int top, int outerWidth); - - bool containsPoint(QPoint position) const; - bool buttonsContainPoint(QPoint position) const; - ButtonType buttonTypeFromPoint(QPoint position) const; - int distanceTo(QPoint position) const; - bool isPointAfter(QPoint position) const; - void moveInAlbum(QPoint to); - QPoint center() const; - void suggestMove(float64 delta, Fn callback); - void finishAnimations(); - - void updateFileRow(int row); - -private: - QRect countRealGeometry() const; - QRect countCurrentGeometry(float64 progress) const; - void prepareCache(QSize size, int shrink); - void drawSimpleFrame(Painter &p, QRect to, QSize size) const; - - Ui::GroupMediaLayout _layout; - std::optional _animateFromGeometry; - const QImage _fullPreview; - const int _shrinkSize = 0; - QPixmap _albumImage; - QImage _albumCache; - QPoint _albumPosition; - RectParts _albumCorners = RectPart::None; - QPixmap _photo; - QPixmap _fileThumb; - QString _name; - QString _status; - int _nameWidth = 0; - int _statusWidth = 0; - bool _isVideo = false; - float64 _suggestedMove = 0.; - Ui::Animations::Simple _suggestedMoveAnimation; - int _lastShrinkValue = 0; - - QRect _lastRectOfButtons; - - object_ptr _editMedia = nullptr; - object_ptr _deleteMedia = nullptr; - -}; - -AlbumThumb::AlbumThumb( - const Ui::PreparedFile &file, - const Ui::GroupMediaLayout &layout, - QWidget *parent, - Fn editCallback, - Fn deleteCallback) -: _layout(layout) -, _fullPreview(file.preview) -, _shrinkSize(int(std::ceil(st::historyMessageRadius / 1.4))) -, _isVideo(file.type == Ui::PreparedFile::AlbumType::Video) { - Expects(!_fullPreview.isNull()); - - moveToLayout(layout); - - using Option = Images::Option; - const auto previewWidth = _fullPreview.width(); - const auto previewHeight = _fullPreview.height(); - const auto imageWidth = std::max( - previewWidth / cIntRetinaFactor(), - st::minPhotoSize); - const auto imageHeight = std::max( - previewHeight / cIntRetinaFactor(), - st::minPhotoSize); - _photo = App::pixmapFromImageInPlace(Images::prepare( - _fullPreview, - previewWidth, - previewHeight, - Option::RoundedLarge | Option::RoundedAll, - imageWidth, - imageHeight)); - - const auto idealSize = st::sendMediaFileThumbSize * cIntRetinaFactor(); - const auto fileThumbSize = (previewWidth > previewHeight) - ? QSize(previewWidth * idealSize / previewHeight, idealSize) - : QSize(idealSize, previewHeight * idealSize / previewWidth); - _fileThumb = App::pixmapFromImageInPlace(Images::prepare( - _fullPreview, - fileThumbSize.width(), - fileThumbSize.height(), - Option::RoundedSmall | Option::RoundedAll, - st::sendMediaFileThumbSize, - st::sendMediaFileThumbSize - )); - - const auto availableFileWidth = st::sendMediaPreviewSize - - st::sendMediaFileThumbSkip - - st::sendMediaFileThumbSize - // Right buttons. - - st::sendBoxAlbumGroupButtonFile.width * 2 - - st::sendBoxAlbumGroupEditInternalSkip * 2 - - st::sendBoxAlbumGroupSkipRight; - const auto filepath = file.path; - if (filepath.isEmpty()) { - _name = filedialogDefaultName( - qsl("image"), - qsl(".png"), - QString(), - true); - _status = qsl("%1x%2").arg( - _fullPreview.width() - ).arg( - _fullPreview.height() - ); - } else { - auto fileinfo = QFileInfo(filepath); - _name = fileinfo.fileName(); - _status = Ui::FormatSizeText(fileinfo.size()); - } - _nameWidth = st::semiboldFont->width(_name); - if (_nameWidth > availableFileWidth) { - _name = st::semiboldFont->elided( - _name, - availableFileWidth, - Qt::ElideMiddle); - _nameWidth = st::semiboldFont->width(_name); - } - _statusWidth = st::normalFont->width(_status); - - _editMedia.create(parent, st::sendBoxAlbumGroupButtonFile); - _deleteMedia.create(parent, st::sendBoxAlbumGroupButtonFile); - - const auto duration = st::historyAttach.ripple.hideDuration; - _editMedia->setClickedCallback(App::LambdaDelayed( - duration, - parent, - std::move(editCallback))); - _deleteMedia->setClickedCallback(App::LambdaDelayed( - duration, - parent, - std::move(deleteCallback))); - - _editMedia->setIconOverride(&st::editMediaButtonIconFile); - _deleteMedia->setIconOverride(&st::sendBoxAlbumGroupDeleteButtonIconFile); - - updateFileRow(-1); -} - -void AlbumThumb::updateFileRow(int row) { - if (row < 0) { - _editMedia->hide(); - _deleteMedia->hide(); - return; - } - _editMedia->show(); - _deleteMedia->show(); - - const auto fileHeight = st::sendMediaFileThumbSize - + st::sendMediaFileThumbSkip; - - const auto top = row * fileHeight + st::sendBoxAlbumGroupSkipTop; - const auto size = st::editMediaButtonSize; - - auto right = st::sendBoxAlbumGroupSkipRight + size; - _deleteMedia->moveToRight(right, top); - right += st::sendBoxAlbumGroupEditInternalSkip + size; - _editMedia->moveToRight(right, top); -} - -void AlbumThumb::resetLayoutAnimation() { - _animateFromGeometry = std::nullopt; -} - -void AlbumThumb::animateLayoutToInitial() { - _animateFromGeometry = countRealGeometry(); - _suggestedMove = 0.; - _albumPosition = QPoint(0, 0); -} - -void AlbumThumb::moveToLayout(const Ui::GroupMediaLayout &layout) { - animateLayoutToInitial(); - _layout = layout; - - const auto width = _layout.geometry.width(); - const auto height = _layout.geometry.height(); - _albumCorners = Ui::GetCornersFromSides(_layout.sides); - using Option = Images::Option; - const auto options = Option::Smooth - | Option::RoundedLarge - | ((_albumCorners & RectPart::TopLeft) - ? Option::RoundedTopLeft - : Option::None) - | ((_albumCorners & RectPart::TopRight) - ? Option::RoundedTopRight - : Option::None) - | ((_albumCorners & RectPart::BottomLeft) - ? Option::RoundedBottomLeft - : Option::None) - | ((_albumCorners & RectPart::BottomRight) - ? Option::RoundedBottomRight - : Option::None); - const auto pixSize = Ui::GetImageScaleSizeForGeometry( - { _fullPreview.width(), _fullPreview.height() }, - { width, height }); - const auto pixWidth = pixSize.width() * cIntRetinaFactor(); - const auto pixHeight = pixSize.height() * cIntRetinaFactor(); - - _albumImage = App::pixmapFromImageInPlace(Images::prepare( - _fullPreview, - pixWidth, - pixHeight, - options, - width, - height)); -} - -int AlbumThumb::photoHeight() const { - return _photo.height() / cIntRetinaFactor(); -} - -void AlbumThumb::paintInAlbum( - Painter &p, - int left, - int top, - float64 shrinkProgress, - float64 moveProgress) { - const auto shrink = anim::interpolate(0, _shrinkSize, shrinkProgress); - _lastShrinkValue = shrink; - const auto geometry = countCurrentGeometry(moveProgress); - const auto x = left + geometry.x(); - const auto y = top + geometry.y(); - if (shrink > 0 || moveProgress < 1.) { - const auto size = geometry.size(); - if (shrinkProgress < 1 && _albumCorners != RectPart::None) { - prepareCache(size, shrink); - p.drawImage(x, y, _albumCache); - } else { - const auto to = QRect({ x, y }, size).marginsRemoved( - { shrink, shrink, shrink, shrink } - ); - drawSimpleFrame(p, to, size); - } - } else { - p.drawPixmap(x, y, _albumImage); - } - if (_isVideo) { - const auto inner = QRect( - x + (geometry.width() - st::msgFileSize) / 2, - y + (geometry.height() - st::msgFileSize) / 2, - st::msgFileSize, - st::msgFileSize); - { - PainterHighQualityEnabler hq(p); - p.setPen(Qt::NoPen); - p.setBrush(st::msgDateImgBg); - p.drawEllipse(inner); - } - st::historyFileThumbPlay.paintInCenter(p, inner); - } - - _lastRectOfButtons = PaintAlbumThumbButtons( - p, - { x, y }, - geometry.width(), - shrinkProgress); -} - -void AlbumThumb::prepareCache(QSize size, int shrink) { - const auto width = std::max( - _layout.geometry.width(), - _animateFromGeometry ? _animateFromGeometry->width() : 0); - const auto height = std::max( - _layout.geometry.height(), - _animateFromGeometry ? _animateFromGeometry->height() : 0); - const auto cacheSize = QSize(width, height) * cIntRetinaFactor(); - - if (_albumCache.width() < cacheSize.width() - || _albumCache.height() < cacheSize.height()) { - _albumCache = QImage(cacheSize, QImage::Format_ARGB32_Premultiplied); - } - _albumCache.fill(Qt::transparent); - { - Painter p(&_albumCache); - const auto to = QRect(QPoint(), size).marginsRemoved( - { shrink, shrink, shrink, shrink } - ); - drawSimpleFrame(p, to, size); - } - Images::prepareRound( - _albumCache, - ImageRoundRadius::Large, - _albumCorners, - QRect(QPoint(), size * cIntRetinaFactor())); - _albumCache.setDevicePixelRatio(cRetinaFactor()); -} - -void AlbumThumb::drawSimpleFrame(Painter &p, QRect to, QSize size) const { - const auto fullWidth = _fullPreview.width(); - const auto fullHeight = _fullPreview.height(); - const auto previewSize = Ui::GetImageScaleSizeForGeometry( - { fullWidth, fullHeight }, - { size.width(), size.height() }); - const auto previewWidth = previewSize.width() * cIntRetinaFactor(); - const auto previewHeight = previewSize.height() * cIntRetinaFactor(); - const auto width = size.width() * cIntRetinaFactor(); - const auto height = size.height() * cIntRetinaFactor(); - const auto scaleWidth = to.width() / float64(width); - const auto scaleHeight = to.height() / float64(height); - const auto Round = [](float64 value) { - return int(std::round(value)); - }; - const auto [from, fillBlack] = [&] { - if (previewWidth < width && previewHeight < height) { - const auto toWidth = Round(previewWidth * scaleWidth); - const auto toHeight = Round(previewHeight * scaleHeight); - return std::make_pair( - QRect(0, 0, fullWidth, fullHeight), - QMargins( - (to.width() - toWidth) / 2, - (to.height() - toHeight) / 2, - to.width() - toWidth - (to.width() - toWidth) / 2, - to.height() - toHeight - (to.height() - toHeight) / 2)); - } else if (previewWidth * height > previewHeight * width) { - if (previewHeight >= height) { - const auto takeWidth = previewWidth * height / previewHeight; - const auto useWidth = fullWidth * width / takeWidth; - return std::make_pair( - QRect( - (fullWidth - useWidth) / 2, - 0, - useWidth, - fullHeight), - QMargins(0, 0, 0, 0)); - } else { - const auto takeWidth = previewWidth; - const auto useWidth = fullWidth * width / takeWidth; - const auto toHeight = Round(previewHeight * scaleHeight); - const auto toSkip = (to.height() - toHeight) / 2; - return std::make_pair( - QRect( - (fullWidth - useWidth) / 2, - 0, - useWidth, - fullHeight), - QMargins( - 0, - toSkip, - 0, - to.height() - toHeight - toSkip)); - } - } else { - if (previewWidth >= width) { - const auto takeHeight = previewHeight * width / previewWidth; - const auto useHeight = fullHeight * height / takeHeight; - return std::make_pair( - QRect( - 0, - (fullHeight - useHeight) / 2, - fullWidth, - useHeight), - QMargins(0, 0, 0, 0)); - } else { - const auto takeHeight = previewHeight; - const auto useHeight = fullHeight * height / takeHeight; - const auto toWidth = Round(previewWidth * scaleWidth); - const auto toSkip = (to.width() - toWidth) / 2; - return std::make_pair( - QRect( - 0, - (fullHeight - useHeight) / 2, - fullWidth, - useHeight), - QMargins( - toSkip, - 0, - to.width() - toWidth - toSkip, - 0)); - } - } - }(); - - p.drawImage(to.marginsRemoved(fillBlack), _fullPreview, from); - if (fillBlack.top() > 0) { - p.fillRect(to.x(), to.y(), to.width(), fillBlack.top(), st::imageBg); - } - if (fillBlack.bottom() > 0) { - p.fillRect( - to.x(), - to.y() + to.height() - fillBlack.bottom(), - to.width(), - fillBlack.bottom(), - st::imageBg); - } - if (fillBlack.left() > 0) { - p.fillRect( - to.x(), - to.y() + fillBlack.top(), - fillBlack.left(), - to.height() - fillBlack.top() - fillBlack.bottom(), - st::imageBg); - } - if (fillBlack.right() > 0) { - p.fillRect( - to.x() + to.width() - fillBlack.right(), - to.y() + fillBlack.top(), - fillBlack.right(), - to.height() - fillBlack.top() - fillBlack.bottom(), - st::imageBg); - } -} - -void AlbumThumb::paintPhoto(Painter &p, int left, int top, int outerWidth) { - const auto width = _photo.width() / cIntRetinaFactor(); - p.drawPixmapLeft( - left + (st::sendMediaPreviewSize - width) / 2, - top, - outerWidth, - _photo); - - _lastRectOfButtons = PaintAlbumThumbButtons( - p, - { left, top }, - st::sendMediaPreviewSize, - 0); -} - -void AlbumThumb::paintFile(Painter &p, int left, int top, int outerWidth) { - const auto textLeft = left - + st::sendMediaFileThumbSize - + st::sendMediaFileThumbSkip; - - p.drawPixmap(left, top, _fileThumb); - p.setFont(st::semiboldFont); - p.setPen(st::historyFileNameInFg); - p.drawTextLeft( - textLeft, - top + st::sendMediaFileNameTop, - outerWidth, - _name, - _nameWidth); - p.setFont(st::normalFont); - p.setPen(st::mediaInFg); - p.drawTextLeft( - textLeft, - top + st::sendMediaFileStatusTop, - outerWidth, - _status, - _statusWidth); -} - -bool AlbumThumb::containsPoint(QPoint position) const { - return _layout.geometry.contains(position); -} - -bool AlbumThumb::buttonsContainPoint(QPoint position) const { - return _lastRectOfButtons.contains(position); -} - -ButtonType AlbumThumb::buttonTypeFromPoint(QPoint position) const { - if (!buttonsContainPoint(position)) { - return ButtonType::None; - } - return (position.x() < _lastRectOfButtons.center().x()) - ? ButtonType::Edit - : ButtonType::Delete; -} - -int AlbumThumb::distanceTo(QPoint position) const { - const auto delta = (_layout.geometry.center() - position); - return QPoint::dotProduct(delta, delta); -} - -bool AlbumThumb::isPointAfter(QPoint position) const { - return position.x() > _layout.geometry.center().x(); -} - -void AlbumThumb::moveInAlbum(QPoint to) { - _albumPosition = to; -} - -QPoint AlbumThumb::center() const { - auto realGeometry = _layout.geometry; - realGeometry.moveTopLeft(realGeometry.topLeft() + _albumPosition); - return realGeometry.center(); -} - -void AlbumThumb::suggestMove(float64 delta, Fn callback) { - if (_suggestedMove != delta) { - _suggestedMoveAnimation.start( - std::move(callback), - _suggestedMove, - delta, - kShrinkDuration); - _suggestedMove = delta; - } -} - -QRect AlbumThumb::countRealGeometry() const { - const auto addLeft = int(std::round( - _suggestedMoveAnimation.value(_suggestedMove) * _lastShrinkValue)); - const auto current = _layout.geometry; - const auto realTopLeft = current.topLeft() - + _albumPosition - + QPoint(addLeft, 0); - return { realTopLeft, current.size() }; -} - -QRect AlbumThumb::countCurrentGeometry(float64 progress) const { - const auto now = countRealGeometry(); - if (_animateFromGeometry && progress < 1.) { - return { - anim::interpolate(_animateFromGeometry->x(), now.x(), progress), - anim::interpolate(_animateFromGeometry->y(), now.y(), progress), - anim::interpolate(_animateFromGeometry->width(), now.width(), progress), - anim::interpolate(_animateFromGeometry->height(), now.height(), progress) - }; - } - return now; -} - -void AlbumThumb::finishAnimations() { - _suggestedMoveAnimation.stop(); -} - SingleMediaPreview *SingleMediaPreview::Create( QWidget *parent, not_null controller, @@ -969,155 +348,6 @@ rpl::producer SingleMediaPreview::desiredHeightValue() const { return rpl::single(st::boxPhotoPadding.top() + _previewHeight); } -SingleFilePreview::SingleFilePreview( - QWidget *parent, - const Ui::PreparedFile &file) -: RpWidget(parent) { - preparePreview(file); -} - -void SingleFilePreview::prepareThumb(const QImage &preview) { - if (preview.isNull()) { - return; - } - - auto originalWidth = preview.width(); - auto originalHeight = preview.height(); - auto thumbWidth = st::msgFileThumbSize; - if (originalWidth > originalHeight) { - thumbWidth = (originalWidth * st::msgFileThumbSize) - / originalHeight; - } - auto options = Images::Option::Smooth - | Images::Option::RoundedSmall - | Images::Option::RoundedTopLeft - | Images::Option::RoundedTopRight - | Images::Option::RoundedBottomLeft - | Images::Option::RoundedBottomRight; - _fileThumb = App::pixmapFromImageInPlace(Images::prepare( - preview, - thumbWidth * cIntRetinaFactor(), - 0, - options, - st::msgFileThumbSize, - st::msgFileThumbSize)); -} - -void SingleFilePreview::preparePreview(const Ui::PreparedFile &file) { - auto preview = QImage(); - if (const auto image = std::get_if( - &file.information->media)) { - preview = image->data; - } else if (const auto video = std::get_if( - &file.information->media)) { - preview = video->thumbnail; - } - prepareThumb(preview); - const auto filepath = file.path; - if (filepath.isEmpty()) { - auto filename = filedialogDefaultName( - qsl("image"), - qsl(".png"), - QString(), - true); - _nameText.setText( - st::semiboldTextStyle, - filename, - Ui::NameTextOptions()); - _statusText = qsl("%1x%2").arg(preview.width()).arg(preview.height()); - _statusWidth = qMax(_nameText.maxWidth(), st::normalFont->width(_statusText)); - _fileIsImage = true; - } else { - auto fileinfo = QFileInfo(filepath); - auto filename = fileinfo.fileName(); - _fileIsImage = fileIsImage(filename, Core::MimeTypeForFile(fileinfo).name()); - - auto songTitle = QString(); - auto songPerformer = QString(); - if (file.information) { - if (const auto song = std::get_if( - &file.information->media)) { - songTitle = song->title; - songPerformer = song->performer; - _fileIsAudio = true; - } - } - - const auto nameString = DocumentData::ComposeNameString( - filename, - songTitle, - songPerformer); - _nameText.setText( - st::semiboldTextStyle, - nameString, - Ui::NameTextOptions()); - _statusText = Ui::FormatSizeText(fileinfo.size()); - _statusWidth = qMax( - _nameText.maxWidth(), - st::normalFont->width(_statusText)); - } -} - -void SingleFilePreview::paintEvent(QPaintEvent *e) { - Painter p(this); - - auto w = width() - st::boxPhotoPadding.left() - st::boxPhotoPadding.right(); - auto h = _fileThumb.isNull() ? (st::msgFilePadding.top() + st::msgFileSize + st::msgFilePadding.bottom()) : (st::msgFileThumbPadding.top() + st::msgFileThumbSize + st::msgFileThumbPadding.bottom()); - auto nameleft = 0, nametop = 0, nameright = 0, statustop = 0, linktop = 0; - if (_fileThumb.isNull()) { - nameleft = st::msgFilePadding.left() + st::msgFileSize + st::msgFilePadding.right(); - nametop = st::msgFileNameTop; - nameright = st::msgFilePadding.left(); - statustop = st::msgFileStatusTop; - } else { - nameleft = st::msgFileThumbPadding.left() + st::msgFileThumbSize + st::msgFileThumbPadding.right(); - nametop = st::msgFileThumbNameTop; - nameright = st::msgFileThumbPadding.left(); - statustop = st::msgFileThumbStatusTop; - linktop = st::msgFileThumbLinkTop; - } - auto namewidth = w - nameleft - (_fileThumb.isNull() ? st::msgFilePadding.left() : st::msgFileThumbPadding.left()); - int32 x = (width() - w) / 2, y = st::boxPhotoPadding.top(); - - App::roundRect(p, x, y, w, h, st::msgOutBg, MessageOutCorners, &st::msgOutShadow); - - if (_fileThumb.isNull()) { - QRect inner(style::rtlrect(x + st::msgFilePadding.left(), y + st::msgFilePadding.top(), st::msgFileSize, st::msgFileSize, width())); - p.setPen(Qt::NoPen); - p.setBrush(st::msgFileOutBg); - - { - PainterHighQualityEnabler hq(p); - p.drawEllipse(inner); - } - - auto &icon = _fileIsAudio - ? st::historyFileOutPlay - : _fileIsImage - ? st::historyFileOutImage - : st::historyFileOutDocument; - icon.paintInCenter(p, inner); - } else { - QRect rthumb(style::rtlrect(x + st::msgFileThumbPadding.left(), y + st::msgFileThumbPadding.top(), st::msgFileThumbSize, st::msgFileThumbSize, width())); - p.drawPixmap(rthumb.topLeft(), _fileThumb); - } - p.setFont(st::semiboldFont); - p.setPen(st::historyFileNameOutFg); - _nameText.drawLeftElided(p, x + nameleft, y + nametop, namewidth, width()); - - auto &status = st::mediaOutFg; - p.setFont(st::normalFont); - p.setPen(status); - p.drawTextLeft(x + nameleft, y + statustop, width(), _statusText); -} - -rpl::producer SingleFilePreview::desiredHeightValue() const { - auto h = _fileThumb.isNull() - ? (st::msgFilePadding.top() + st::msgFileSize + st::msgFilePadding.bottom()) - : (st::msgFileThumbPadding.top() + st::msgFileThumbSize + st::msgFileThumbPadding.bottom()); - return rpl::single(st::boxPhotoPadding.top() + h + st::msgShadow); -} - rpl::producer FieldPlaceholder( const Ui::PreparedList &list, SendFilesWay way) { @@ -1130,536 +360,6 @@ rpl::producer FieldPlaceholder( } // namespace -class SendFilesBox::AlbumPreview : public Ui::RpWidget { -public: - AlbumPreview( - QWidget *parent, - const Ui::PreparedList &list, - SendFilesWay way); - - void setSendWay(SendFilesWay way); - std::vector takeOrder(); - - auto thumbDeleted() { - return _thumbDeleted.events(); - } - - auto thumbChanged() { - return _thumbChanged.events(); - } - -protected: - void paintEvent(QPaintEvent *e) override; - void mousePressEvent(QMouseEvent *e) override; - void mouseMoveEvent(QMouseEvent *e) override; - void mouseReleaseEvent(QMouseEvent *e) override; - -private: - int countLayoutHeight( - const std::vector &layout) const; - std::vector generateOrderedLayout() const; - std::vector defaultOrder() const; - void prepareThumbs(); - void updateSizeAnimated(const std::vector &layout); - void updateSize(); - void updateFileRows(); - - int thumbIndex(AlbumThumb *thumb); - AlbumThumb *thumbUnderCursor(); - void deleteThumbByIndex(int index); - void changeThumbByIndex(int index); - void thumbButtonsCallback( - not_null thumb, - ButtonType type); - - void paintAlbum(Painter &p) const; - void paintPhotos(Painter &p, QRect clip) const; - void paintFiles(Painter &p, QRect clip) const; - - void applyCursor(style::cursor cursor); - int contentLeft() const; - int contentTop() const; - AlbumThumb *findThumb(QPoint position) const; - not_null findClosestThumb(QPoint position) const; - void updateSuggestedDrag(QPoint position); - int orderIndex(not_null thumb) const; - void cancelDrag(); - void finishDrag(); - - const Ui::PreparedList &_list; - SendFilesWay _sendWay = SendFilesWay::Files; - style::cursor _cursor = style::cur_default; - std::vector _order; - std::vector> _thumbs; - int _thumbsHeight = 0; - int _photosHeight = 0; - int _filesHeight = 0; - - AlbumThumb *_draggedThumb = nullptr; - AlbumThumb *_suggestedThumb = nullptr; - AlbumThumb *_paintedAbove = nullptr; - QPoint _draggedStartPosition; - - rpl::event_stream _thumbDeleted; - rpl::event_stream _thumbChanged; - - mutable Ui::Animations::Simple _thumbsHeightAnimation; - mutable Ui::Animations::Simple _shrinkAnimation; - mutable Ui::Animations::Simple _finishDragAnimation; - -}; - -SendFilesBox::AlbumPreview::AlbumPreview( - QWidget *parent, - const Ui::PreparedList &list, - SendFilesWay way) -: RpWidget(parent) -, _list(list) -, _sendWay(way) { - setMouseTracking(true); - prepareThumbs(); - updateSize(); - updateFileRows(); -} - -void SendFilesBox::AlbumPreview::setSendWay(SendFilesWay way) { - if (_sendWay != way) { - cancelDrag(); - _sendWay = way; - } - updateSize(); - updateFileRows(); - update(); -} - -void SendFilesBox::AlbumPreview::updateFileRows() { - Expects(_order.size() == _thumbs.size()); - const auto isFile = (_sendWay == SendFilesWay::Files); - for (auto i = 0; i < _order.size(); i++) { - _thumbs[i]->updateFileRow(isFile ? _order[i] : -1); - } -} - -std::vector SendFilesBox::AlbumPreview::takeOrder() { - auto reordered = std::vector>(); - reordered.reserve(_thumbs.size()); - for (auto index : _order) { - reordered.push_back(std::move(_thumbs[index])); - } - _thumbs = std::move(reordered); - return std::exchange(_order, defaultOrder()); -} - -auto SendFilesBox::AlbumPreview::generateOrderedLayout() const --> std::vector { - auto sizes = ranges::view::all( - _order - ) | ranges::view::transform([&](int index) { - return _list.files[index].shownDimensions; - }) | ranges::to_vector; - - auto layout = Ui::LayoutMediaGroup( - sizes, - st::sendMediaPreviewSize, - st::historyGroupWidthMin / 2, - st::historyGroupSkip / 2); - Assert(layout.size() == _order.size()); - return layout; -} - -std::vector SendFilesBox::AlbumPreview::defaultOrder() const { - const auto count = int(_list.files.size()); - return ranges::view::ints(0, count) | ranges::to_vector; -} - -void SendFilesBox::AlbumPreview::prepareThumbs() { - _order = defaultOrder(); - - const auto count = int(_list.files.size()); - const auto layout = generateOrderedLayout(); - _thumbs.reserve(count); - for (auto i = 0; i != count; ++i) { - _thumbs.push_back(std::make_unique( - _list.files[i], - layout[i], - this, - [=] { changeThumbByIndex(thumbIndex(thumbUnderCursor())); }, - [=] { deleteThumbByIndex(thumbIndex(thumbUnderCursor())); })); - } - _thumbsHeight = countLayoutHeight(layout); - _photosHeight = ranges::accumulate(ranges::view::all( - _thumbs - ) | ranges::view::transform([](const auto &thumb) { - return thumb->photoHeight(); - }), 0) + (count - 1) * st::sendMediaPreviewPhotoSkip; - - _filesHeight = count * st::sendMediaFileThumbSize - + (count - 1) * st::sendMediaFileThumbSkip; -} - -int SendFilesBox::AlbumPreview::contentLeft() const { - return (st::boxWideWidth - st::sendMediaPreviewSize) / 2; -} - -int SendFilesBox::AlbumPreview::contentTop() const { - return 0; -} - -AlbumThumb *SendFilesBox::AlbumPreview::findThumb(QPoint position) const { - position -= QPoint(contentLeft(), contentTop()); - - auto top = 0; - const auto isPhotosWay = (_sendWay == SendFilesWay::Photos); - const auto skip = isPhotosWay - ? st::sendMediaPreviewPhotoSkip - : st::sendMediaFileThumbSkip; - auto find = [&](const auto &thumb) { - if (_sendWay == SendFilesWay::Album) { - return thumb->containsPoint(position); - } else if (isPhotosWay || _sendWay == SendFilesWay::Files) { - const auto bottom = top + (isPhotosWay - ? thumb->photoHeight() - : st::sendMediaFileThumbSize); - const auto isUnderTop = (position.y() > top); - top = bottom + skip; - return isUnderTop && (position.y() < bottom); - } - return false; - }; - - const auto i = ranges::find_if(_thumbs, std::move(find)); - return (i == _thumbs.end()) ? nullptr : i->get(); -} - -not_null SendFilesBox::AlbumPreview::findClosestThumb( - QPoint position) const { - Expects(_draggedThumb != nullptr); - - if (const auto exact = findThumb(position)) { - return exact; - } - auto result = _draggedThumb; - auto distance = _draggedThumb->distanceTo(position); - for (const auto &thumb : _thumbs) { - const auto check = thumb->distanceTo(position); - if (check < distance) { - distance = check; - result = thumb.get(); - } - } - return result; -} - -int SendFilesBox::AlbumPreview::orderIndex( - not_null thumb) const { - const auto i = ranges::find_if(_order, [&](int index) { - return (_thumbs[index].get() == thumb); - }); - Assert(i != _order.end()); - return int(i - _order.begin()); -} - -void SendFilesBox::AlbumPreview::cancelDrag() { - _thumbsHeightAnimation.stop(); - _finishDragAnimation.stop(); - _shrinkAnimation.stop(); - if (_draggedThumb) { - _draggedThumb->moveInAlbum({ 0, 0 }); - _draggedThumb = nullptr; - } - if (_suggestedThumb) { - const auto suggestedIndex = orderIndex(_suggestedThumb); - if (suggestedIndex > 0) { - _thumbs[_order[suggestedIndex - 1]]->suggestMove(0., [] {}); - } - if (suggestedIndex < int(_order.size() - 1)) { - _thumbs[_order[suggestedIndex + 1]]->suggestMove(0., [] {}); - } - _suggestedThumb->suggestMove(0., [] {}); - _suggestedThumb->finishAnimations(); - _suggestedThumb = nullptr; - } - _paintedAbove = nullptr; - update(); -} - -void SendFilesBox::AlbumPreview::finishDrag() { - Expects(_draggedThumb != nullptr); - Expects(_suggestedThumb != nullptr); - - if (_suggestedThumb != _draggedThumb) { - const auto currentIndex = orderIndex(_draggedThumb); - const auto newIndex = orderIndex(_suggestedThumb); - const auto delta = (currentIndex < newIndex) ? 1 : -1; - const auto realIndex = _order[currentIndex]; - for (auto i = currentIndex; i != newIndex; i += delta) { - _order[i] = _order[i + delta]; - } - _order[newIndex] = realIndex; - const auto layout = generateOrderedLayout(); - for (auto i = 0, count = int(_order.size()); i != count; ++i) { - _thumbs[_order[i]]->moveToLayout(layout[i]); - } - _finishDragAnimation.start([=] { update(); }, 0., 1., kDragDuration); - - updateSizeAnimated(layout); - } else { - for (const auto &thumb : _thumbs) { - thumb->resetLayoutAnimation(); - } - _draggedThumb->animateLayoutToInitial(); - _finishDragAnimation.start([=] { update(); }, 0., 1., kDragDuration); - } -} - -int SendFilesBox::AlbumPreview::countLayoutHeight( - const std::vector &layout) const { - const auto accumulator = [](int current, const auto &item) { - return std::max(current, item.geometry.y() + item.geometry.height()); - }; - return ranges::accumulate(layout, 0, accumulator); -} - -void SendFilesBox::AlbumPreview::updateSizeAnimated( - const std::vector &layout) { - const auto newHeight = countLayoutHeight(layout); - if (newHeight != _thumbsHeight) { - _thumbsHeightAnimation.start( - [=] { updateSize(); }, - _thumbsHeight, - newHeight, - kDragDuration); - _thumbsHeight = newHeight; - } -} - -void SendFilesBox::AlbumPreview::updateSize() { - const auto newHeight = [&] { - switch (_sendWay) { - case SendFilesWay::Album: - return int(std::round(_thumbsHeightAnimation.value( - _thumbsHeight))); - case SendFilesWay::Photos: return _photosHeight; - case SendFilesWay::Files: return _filesHeight; - } - Unexpected("Send way in SendFilesBox::AlbumPreview::updateSize"); - }(); - if (height() != newHeight) { - resize(st::boxWideWidth, newHeight); - } -} - -void SendFilesBox::AlbumPreview::paintEvent(QPaintEvent *e) { - Painter p(this); - - switch (_sendWay) { - case SendFilesWay::Album: paintAlbum(p); break; - case SendFilesWay::Photos: paintPhotos(p, e->rect()); break; - case SendFilesWay::Files: paintFiles(p, e->rect()); break; - } -} - -void SendFilesBox::AlbumPreview::paintAlbum(Painter &p) const { - const auto shrink = _shrinkAnimation.value(_draggedThumb ? 1. : 0.); - const auto moveProgress = _finishDragAnimation.value(1.); - const auto left = contentLeft(); - const auto top = contentTop(); - for (const auto &thumb : _thumbs) { - if (thumb.get() != _paintedAbove) { - thumb->paintInAlbum(p, left, top, shrink, moveProgress); - } - } - if (_paintedAbove) { - _paintedAbove->paintInAlbum(p, left, top, shrink, moveProgress); - } -} - -void SendFilesBox::AlbumPreview::paintPhotos(Painter &p, QRect clip) const { - const auto left = (st::boxWideWidth - st::sendMediaPreviewSize) / 2; - auto top = 0; - const auto outerWidth = width(); - for (const auto &thumb : _thumbs) { - const auto bottom = top + thumb->photoHeight(); - const auto guard = gsl::finally([&] { - top = bottom + st::sendMediaPreviewPhotoSkip; - }); - if (top >= clip.y() + clip.height()) { - break; - } else if (bottom <= clip.y()) { - continue; - } - thumb->paintPhoto(p, left, top, outerWidth); - } -} - -void SendFilesBox::AlbumPreview::paintFiles(Painter &p, QRect clip) const { - const auto fileHeight = st::sendMediaFileThumbSize - + st::sendMediaFileThumbSkip; - const auto bottom = clip.y() + clip.height(); - const auto from = floorclamp(clip.y(), fileHeight, 0, _thumbs.size()); - const auto till = ceilclamp(bottom, fileHeight, 0, _thumbs.size()); - const auto left = (st::boxWideWidth - st::sendMediaPreviewSize) / 2; - const auto outerWidth = width(); - - auto top = from * fileHeight; - for (auto i = from; i != till; ++i) { - _thumbs[i]->paintFile(p, left, top, outerWidth); - top += fileHeight; - } -} - -int SendFilesBox::AlbumPreview::thumbIndex(AlbumThumb *thumb) { - if (!thumb) { - return -1; - } - const auto thumbIt = ranges::find_if(_thumbs, [&](auto &t) { - return t.get() == thumb; - }); - Expects(thumbIt != _thumbs.end()); - return std::distance(_thumbs.begin(), thumbIt); -} - -AlbumThumb *SendFilesBox::AlbumPreview::thumbUnderCursor() { - return findThumb(mapFromGlobal(QCursor::pos())); -} - -void SendFilesBox::AlbumPreview::deleteThumbByIndex(int index) { - if (index < 0) { - return; - } - const auto orderIt = ranges::find(_order, index); - Expects(orderIt != _order.end()); - - _order.erase(orderIt); - ranges::for_each(_order, [=](auto &i) { - if (i > index) { - i--; - } - }); - _thumbDeleted.fire(std::move(index)); -} - -void SendFilesBox::AlbumPreview::changeThumbByIndex(int index) { - if (index < 0) { - return; - } - _thumbChanged.fire(std::move(index)); -} - -void SendFilesBox::AlbumPreview::thumbButtonsCallback( - not_null thumb, - ButtonType type) { - const auto index = thumbIndex(thumb); - - switch (type) { - case ButtonType::None: return; - case ButtonType::Edit: changeThumbByIndex(index); break; - case ButtonType::Delete: deleteThumbByIndex(index); break; - } -} - -void SendFilesBox::AlbumPreview::mousePressEvent(QMouseEvent *e) { - if (_finishDragAnimation.animating()) { - return; - } - const auto position = e->pos(); - cancelDrag(); - if (const auto thumb = findThumb(position)) { - if (thumb->buttonsContainPoint(e->pos())) { - thumbButtonsCallback(thumb, thumb->buttonTypeFromPoint(e->pos())); - return; - } - _paintedAbove = _suggestedThumb = _draggedThumb = thumb; - _draggedStartPosition = position; - _shrinkAnimation.start([=] { update(); }, 0., 1., kShrinkDuration); - } -} - -void SendFilesBox::AlbumPreview::mouseMoveEvent(QMouseEvent *e) { - if (_sendWay == SendFilesWay::Files) { - applyCursor(style::cur_default); - return; - } - const auto isAlbum = (_sendWay == SendFilesWay::Album); - if (isAlbum && _draggedThumb) { - const auto position = e->pos(); - _draggedThumb->moveInAlbum(position - _draggedStartPosition); - updateSuggestedDrag(_draggedThumb->center()); - update(); - } else { - const auto thumb = findThumb(e->pos()); - const auto regularCursor = isAlbum - ? style::cur_sizeall - : style::cur_default; - const auto cursor = thumb - ? (thumb->buttonsContainPoint(e->pos()) - ? style::cur_pointer - : regularCursor) - : style::cur_default; - applyCursor(cursor); - } -} - -void SendFilesBox::AlbumPreview::applyCursor(style::cursor cursor) { - if (_cursor != cursor) { - _cursor = cursor; - setCursor(_cursor); - } -} - -void SendFilesBox::AlbumPreview::updateSuggestedDrag(QPoint position) { - auto closest = findClosestThumb(position); - auto closestIndex = orderIndex(closest); - - const auto draggedIndex = orderIndex(_draggedThumb); - const auto closestIsBeforePoint = closest->isPointAfter(position); - if (closestIndex < draggedIndex && closestIsBeforePoint) { - closest = _thumbs[_order[++closestIndex]].get(); - } else if (closestIndex > draggedIndex && !closestIsBeforePoint) { - closest = _thumbs[_order[--closestIndex]].get(); - } - - if (_suggestedThumb == closest) { - return; - } - - const auto last = int(_order.size()) - 1; - if (_suggestedThumb) { - const auto suggestedIndex = orderIndex(_suggestedThumb); - if (suggestedIndex < draggedIndex && suggestedIndex > 0) { - const auto previous = _thumbs[_order[suggestedIndex - 1]].get(); - previous->suggestMove(0., [=] { update(); }); - } else if (suggestedIndex > draggedIndex && suggestedIndex < last) { - const auto next = _thumbs[_order[suggestedIndex + 1]].get(); - next->suggestMove(0., [=] { update(); }); - } - _suggestedThumb->suggestMove(0., [=] { update(); }); - } - _suggestedThumb = closest; - const auto suggestedIndex = closestIndex; - if (_suggestedThumb != _draggedThumb) { - const auto delta = (suggestedIndex < draggedIndex) ? 1. : -1.; - if (delta > 0. && suggestedIndex > 0) { - const auto previous = _thumbs[_order[suggestedIndex - 1]].get(); - previous->suggestMove(-delta, [=] { update(); }); - } else if (delta < 0. && suggestedIndex < last) { - const auto next = _thumbs[_order[suggestedIndex + 1]].get(); - next->suggestMove(-delta, [=] { update(); }); - } - _suggestedThumb->suggestMove(delta, [=] { update(); }); - } -} - -void SendFilesBox::AlbumPreview::mouseReleaseEvent(QMouseEvent *e) { - if (_draggedThumb) { - finishDrag(); - _shrinkAnimation.start([=] { update(); }, 1., 0., kShrinkDuration); - _draggedThumb = nullptr; - _suggestedThumb = nullptr; - update(); - } -} - SendFilesBox::SendFilesBox( QWidget*, not_null controller, @@ -1718,7 +418,7 @@ void SendFilesBox::prepareSingleFilePreview() { _preview = media; initPreview(media->desiredHeightValue()); } else { - const auto preview = Ui::CreateChild(this, file); + const auto preview = Ui::CreateChild(this, file); _compressConfirm = CompressConfirm::None; _preview = preview; initPreview(preview->desiredHeightValue()); @@ -1731,7 +431,7 @@ void SendFilesBox::prepareAlbumPreview() { const auto wrap = Ui::CreateChild( this, st::boxScroll); - _albumPreview = wrap->setOwnedWidget(object_ptr( + _albumPreview = wrap->setOwnedWidget(object_ptr( this, _list, _sendWay->value())); @@ -1807,7 +507,7 @@ void SendFilesBox::addThumbButtonHandlers(not_null wrap) { void SendFilesBox::setupShadows( not_null wrap, - not_null content) { + not_null content) { using namespace rpl::mappers; const auto topShadow = Ui::CreateChild(this); diff --git a/Telegram/SourceFiles/boxes/send_files_box.h b/Telegram/SourceFiles/boxes/send_files_box.h index d8bfc5f99..4b99028d7 100644 --- a/Telegram/SourceFiles/boxes/send_files_box.h +++ b/Telegram/SourceFiles/boxes/send_files_box.h @@ -35,6 +35,8 @@ class RoundButton; class InputField; struct GroupMediaLayout; class EmojiButton; +class AlbumPreview; +enum class SendFilesWay; } // namespace Ui namespace Window { @@ -45,12 +47,6 @@ namespace SendMenu { enum class Type; } // namespace SendMenu -enum class SendFilesWay { - Album, - Photos, - Files, -}; - class SendFilesBox : public Ui::BoxContent { public: enum class SendLimit { @@ -70,7 +66,7 @@ public: void setConfirmedCallback( Fn callback) { @@ -91,8 +87,6 @@ protected: void resizeEvent(QResizeEvent *e) override; private: - class AlbumPreview; - void initSendWay(); void initPreview(rpl::producer desiredPreviewHeight); @@ -101,7 +95,7 @@ private: void setupCaption(); void setupShadows( not_null wrap, - not_null content); + not_null content); void setupEmojiPanel(); void updateEmojiPanelGeometry(); @@ -149,7 +143,7 @@ private: Fn _confirmedCallback; @@ -161,16 +155,16 @@ private: base::unique_qptr _emojiPanel; base::unique_qptr _emojiFilter; - object_ptr> _sendAlbum = { nullptr }; - object_ptr> _sendPhotos = { nullptr }; - object_ptr> _sendFiles = { nullptr }; - std::shared_ptr> _sendWay; + object_ptr> _sendAlbum = { nullptr }; + object_ptr> _sendPhotos = { nullptr }; + object_ptr> _sendFiles = { nullptr }; + std::shared_ptr> _sendWay; rpl::variable _footerHeight = 0; rpl::event_stream<> _albumChanged; QWidget *_preview = nullptr; - AlbumPreview *_albumPreview = nullptr; + Ui::AlbumPreview *_albumPreview = nullptr; int _albumVideosCount = 0; int _albumPhotosCount = 0; diff --git a/Telegram/SourceFiles/boxes/sticker_set_box.cpp b/Telegram/SourceFiles/boxes/sticker_set_box.cpp index d7488d2c5..62f75f797 100644 --- a/Telegram/SourceFiles/boxes/sticker_set_box.cpp +++ b/Telegram/SourceFiles/boxes/sticker_set_box.cpp @@ -26,6 +26,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/emoji_config.h" #include "ui/toast/toast.h" #include "ui/widgets/popup_menu.h" +#include "ui/cached_round_corners.h" #include "lottie/lottie_multi_player.h" #include "lottie/lottie_animation.h" #include "chat_helpers/stickers_lottie.h" @@ -35,7 +36,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "apiwrap.h" #include "mainwidget.h" #include "mainwindow.h" -#include "app.h" #include "styles/style_layers.h" #include "styles/style_chat_helpers.h" #include "styles/style_info.h" @@ -661,7 +661,7 @@ void StickerSetBox::Inner::paintSticker( p.setOpacity(over); auto tl = position; if (rtl()) tl.setX(width() - tl.x() - st::stickersSize.width()); - App::roundRect(p, QRect(tl, st::stickersSize), st::emojiPanHover, StickerHoverCorners); + Ui::FillRoundRect(p, QRect(tl, st::stickersSize), st::emojiPanHover, Ui::StickerHoverCorners); p.setOpacity(1); } diff --git a/Telegram/SourceFiles/boxes/stickers_box.cpp b/Telegram/SourceFiles/boxes/stickers_box.cpp index c70fa623c..5ed95a24b 100644 --- a/Telegram/SourceFiles/boxes/stickers_box.cpp +++ b/Telegram/SourceFiles/boxes/stickers_box.cpp @@ -33,9 +33,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/discrete_sliders.h" #include "ui/widgets/input_fields.h" #include "ui/image/image.h" +#include "ui/cached_round_corners.h" #include "window/window_session_controller.h" #include "main/main_session.h" -#include "app.h" #include "styles/style_layers.h" #include "styles/style_boxes.h" #include "styles/style_chat_helpers.h" @@ -1129,7 +1129,7 @@ void StickersBox::Inner::paintRow(Painter &p, not_null row, int index) { Ui::Shadow::paint(p, rect, width(), st::boxRoundShadow); p.setOpacity(1); - App::roundRect(p, rect, st::boxBg, BoxCorners); + Ui::FillRoundRect(p, rect, st::boxBg, Ui::BoxCorners); p.setOpacity(1. - current); paintFakeButton(p, row, index); @@ -1318,7 +1318,7 @@ void StickersBox::Inner::paintFakeButton(Painter &p, not_null row, int ind auto textWidth = (_section == Section::Installed) ? _undoWidth : _addWidth; auto &text = (_section == Section::Installed) ? _undoText : _addText; auto &textBg = selected ? st.textBgOver : st.textBg; - App::roundRect(p, myrtlrect(rect), textBg, ImageRoundRadius::Small); + Ui::FillRoundRect(p, myrtlrect(rect), textBg, ImageRoundRadius::Small); if (row->ripple) { row->ripple->paint(p, rect.x(), rect.y(), width()); if (row->ripple->empty()) { diff --git a/Telegram/SourceFiles/chat_helpers/bot_keyboard.cpp b/Telegram/SourceFiles/chat_helpers/bot_keyboard.cpp index 375039449..17ee74a6b 100644 --- a/Telegram/SourceFiles/chat_helpers/bot_keyboard.cpp +++ b/Telegram/SourceFiles/chat_helpers/bot_keyboard.cpp @@ -12,8 +12,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_user.h" #include "data/data_session.h" #include "main/main_session.h" +#include "ui/cached_round_corners.h" #include "facades.h" -#include "app.h" #include "styles/style_widgets.h" #include "styles/style_chat.h" @@ -76,7 +76,7 @@ void Style::paintButtonBg( Painter &p, const QRect &rect, float64 howMuchOver) const { - App::roundRect(p, rect, st::botKbBg, BotKeyboardCorners); + Ui::FillRoundRect(p, rect, st::botKbBg, Ui::BotKeyboardCorners); } void Style::paintButtonIcon( diff --git a/Telegram/SourceFiles/chat_helpers/emoji_list_widget.cpp b/Telegram/SourceFiles/chat_helpers/emoji_list_widget.cpp index ec66b0cfc..ea5213b66 100644 --- a/Telegram/SourceFiles/chat_helpers/emoji_list_widget.cpp +++ b/Telegram/SourceFiles/chat_helpers/emoji_list_widget.cpp @@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/shadow.h" #include "ui/emoji_config.h" #include "ui/ui_utility.h" +#include "ui/cached_round_corners.h" #include "lang/lang_keys.h" #include "emoji_suggestions_data.h" #include "emoji_suggestions_helper.h" @@ -20,7 +21,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "core/application.h" #include "window/window_session_controller.h" #include "facades.h" -#include "app.h" #include "styles/style_chat_helpers.h" namespace ChatHelpers { @@ -211,7 +211,7 @@ void EmojiColorPicker::paintEvent(QPaintEvent *e) { return; } Ui::Shadow::paint(p, inner, width(), st::defaultRoundShadow); - App::roundRect(p, inner, st::boxBg, BoxCorners); + Ui::FillRoundRect(p, inner, st::boxBg, Ui::BoxCorners); auto x = st::emojiPanMargins.left() + 2 * st::emojiColorsPadding + _singleSize.width(); if (rtl()) x = width() - x - st::emojiColorsSep; @@ -372,7 +372,7 @@ void EmojiColorPicker::drawVariant(Painter &p, int variant) { if (variant == _selected) { QPoint tl(w); if (rtl()) tl.setX(width() - tl.x() - _singleSize.width()); - App::roundRect(p, QRect(tl, _singleSize), st::emojiPanHover, StickerHoverCorners); + Ui::FillRoundRect(p, QRect(tl, _singleSize), st::emojiPanHover, Ui::StickerHoverCorners); } const auto esize = Ui::Emoji::GetSizeLarge(); Ui::Emoji::Draw( @@ -558,7 +558,7 @@ void EmojiListWidget::paintEvent(QPaintEvent *e) { if (selected) { auto tl = w; if (rtl()) tl.setX(width() - tl.x() - _singleSize.width()); - App::roundRect(p, QRect(tl, _singleSize), st::emojiPanHover, StickerHoverCorners); + Ui::FillRoundRect(p, QRect(tl, _singleSize), st::emojiPanHover, Ui::StickerHoverCorners); } Ui::Emoji::Draw( p, diff --git a/Telegram/SourceFiles/chat_helpers/emoji_suggestions_widget.cpp b/Telegram/SourceFiles/chat_helpers/emoji_suggestions_widget.cpp index d77ebf94e..c3693d3b3 100644 --- a/Telegram/SourceFiles/chat_helpers/emoji_suggestions_widget.cpp +++ b/Telegram/SourceFiles/chat_helpers/emoji_suggestions_widget.cpp @@ -17,11 +17,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/input_fields.h" #include "ui/emoji_config.h" #include "ui/ui_utility.h" +#include "ui/cached_round_corners.h" #include "platform/platform_specific.h" #include "core/application.h" #include "base/event_filter.h" #include "main/main_session.h" -#include "app.h" #include "styles/style_chat_helpers.h" #include @@ -218,11 +218,11 @@ void SuggestionsWidget::paintEvent(QPaintEvent *e) { ? _pressed : _selectedAnimation.value(_selected); if (selected > -1.) { - App::roundRect( + Ui::FillRoundRect( p, QRect(selected * _oneWidth, 0, _oneWidth, _oneWidth), st::emojiPanHover, - StickerHoverCorners); + Ui::StickerHoverCorners); } for (auto i = from; i != till; ++i) { diff --git a/Telegram/SourceFiles/chat_helpers/field_autocomplete.cpp b/Telegram/SourceFiles/chat_helpers/field_autocomplete.cpp index de207c1b5..41bce617a 100644 --- a/Telegram/SourceFiles/chat_helpers/field_autocomplete.cpp +++ b/Telegram/SourceFiles/chat_helpers/field_autocomplete.cpp @@ -29,10 +29,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/scroll_area.h" #include "ui/image/image.h" #include "ui/ui_utility.h" +#include "ui/cached_round_corners.h" #include "base/unixtime.h" #include "window/window_session_controller.h" #include "facades.h" -#include "app.h" #include "styles/style_chat.h" #include "styles/style_widgets.h" #include "styles/style_chat_helpers.h" @@ -686,7 +686,7 @@ void FieldAutocompleteInner::paintEvent(QPaintEvent *e) { if (_sel == index) { QPoint tl(pos); if (rtl()) tl.setX(width() - tl.x() - st::stickerPanSize.width()); - App::roundRect(p, QRect(tl, st::stickerPanSize), st::emojiPanHover, StickerHoverCorners); + Ui::FillRoundRect(p, QRect(tl, st::stickerPanSize), st::emojiPanHover, Ui::StickerHoverCorners); } media->checkStickerSmall(); diff --git a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp index 28b35ff14..bece2cd01 100644 --- a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp +++ b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp @@ -21,6 +21,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/effects/animations.h" #include "ui/effects/ripple_animation.h" #include "ui/image/image.h" +#include "ui/cached_round_corners.h" #include "lottie/lottie_multi_player.h" #include "lottie/lottie_single_player.h" #include "lottie/lottie_animation.h" @@ -38,7 +39,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "main/main_session_settings.h" #include "apiwrap.h" #include "api/api_toggling_media.h" // Api::ToggleFavedSticker -#include "app.h" #include "styles/style_chat_helpers.h" #include "styles/style_window.h" @@ -1537,7 +1537,7 @@ void StickersListWidget::paintStickers(Painter &p, QRect clip) { auto selected = selectedButton ? (selectedButton->section == info.section) : false; auto &textBg = selected ? st::stickersTrendingAdd.textBgOver : st::stickersTrendingAdd.textBg; - App::roundRect(p, myrtlrect(add), textBg, ImageRoundRadius::Small); + Ui::FillRoundRect(p, myrtlrect(add), textBg, ImageRoundRadius::Small); if (set.ripple) { set.ripple->paint(p, add.x(), add.y(), width()); if (set.ripple->empty()) { @@ -1767,7 +1767,7 @@ void StickersListWidget::paintMegagroupEmptySet(Painter &p, int y, bool buttonSe : st::stickerGroupCategoryAdd.textBg; auto button = _megagroupSetButtonRect.translated(0, y); - App::roundRect(p, myrtlrect(button), textBg, ImageRoundRadius::Small); + Ui::FillRoundRect(p, myrtlrect(button), textBg, ImageRoundRadius::Small); if (_megagroupSetButtonRipple) { _megagroupSetButtonRipple->paint(p, button.x(), button.y(), width()); if (_megagroupSetButtonRipple->empty()) { @@ -1847,7 +1847,7 @@ void StickersListWidget::paintSticker(Painter &p, Set &set, int y, int section, if (selected) { auto tl = pos; if (rtl()) tl.setX(width() - tl.x() - _singleSize.width()); - App::roundRect(p, QRect(tl, _singleSize), st::emojiPanHover, StickerHoverCorners); + Ui::FillRoundRect(p, QRect(tl, _singleSize), st::emojiPanHover, Ui::StickerHoverCorners); } media->checkStickerSmall(); diff --git a/Telegram/SourceFiles/chat_helpers/tabbed_selector.cpp b/Telegram/SourceFiles/chat_helpers/tabbed_selector.cpp index b60304b46..1121c7cf4 100644 --- a/Telegram/SourceFiles/chat_helpers/tabbed_selector.cpp +++ b/Telegram/SourceFiles/chat_helpers/tabbed_selector.cpp @@ -18,6 +18,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/popup_menu.h" #include "ui/widgets/scroll_area.h" #include "ui/image/image_prepare.h" +#include "ui/cached_round_corners.h" #include "window/window_session_controller.h" #include "main/main_session.h" #include "main/main_session_settings.h" @@ -521,10 +522,10 @@ void TabbedSelector::paintSlideFrame(Painter &p) { if (_roundRadius > 0) { if (full()) { auto topPart = QRect(0, 0, width(), _tabsSlider->height() + _roundRadius); - App::roundRect(p, topPart, st::emojiPanBg, ImageRoundRadius::Small, RectPart::FullTop | RectPart::NoTopBottom); + Ui::FillRoundRect(p, topPart, st::emojiPanBg, ImageRoundRadius::Small, RectPart::FullTop | RectPart::NoTopBottom); } else { auto topPart = QRect(0, 0, width(), 3 * _roundRadius); - App::roundRect(p, topPart, st::emojiPanBg, ImageRoundRadius::Small, RectPart::FullTop); + Ui::FillRoundRect(p, topPart, st::emojiPanBg, ImageRoundRadius::Small, RectPart::FullTop); } } else if (full()) { p.fillRect(0, 0, width(), _tabsSlider->height(), st::emojiPanBg); @@ -538,15 +539,15 @@ void TabbedSelector::paintContent(Painter &p) { if (_roundRadius > 0) { if (full()) { auto topPart = QRect(0, 0, width(), _tabsSlider->height() + _roundRadius); - App::roundRect(p, topPart, st::emojiPanBg, ImageRoundRadius::Small, RectPart::FullTop | RectPart::NoTopBottom); + Ui::FillRoundRect(p, topPart, st::emojiPanBg, ImageRoundRadius::Small, RectPart::FullTop | RectPart::NoTopBottom); } else { auto topPart = QRect(0, 0, width(), 3 * _roundRadius); - App::roundRect(p, topPart, st::emojiPanBg, ImageRoundRadius::Small, RectPart::FullTop); + Ui::FillRoundRect(p, topPart, st::emojiPanBg, ImageRoundRadius::Small, RectPart::FullTop); } auto bottomPart = QRect(0, _footerTop - _roundRadius, width(), st::emojiFooterHeight + _roundRadius); auto bottomParts = RectPart::NoTopBottom | RectPart::FullBottom; - App::roundRect(p, bottomPart, bottomBg, ImageRoundRadius::Small, bottomParts); + Ui::FillRoundRect(p, bottomPart, bottomBg, ImageRoundRadius::Small, bottomParts); } else { if (full()) { p.fillRect(0, 0, width(), _tabsSlider->height(), st::emojiPanBg); diff --git a/Telegram/SourceFiles/core/core_settings.cpp b/Telegram/SourceFiles/core/core_settings.cpp index a7b0b124d..41d138ebf 100644 --- a/Telegram/SourceFiles/core/core_settings.cpp +++ b/Telegram/SourceFiles/core/core_settings.cpp @@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "boxes/send_files_box.h" #include "ui/widgets/input_fields.h" +#include "ui/chat/attach/attach_common.h" #include "storage/serialize_common.h" #include "window/themes/window_theme.h" #include "window/section_widget.h" @@ -18,7 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace Core { Settings::Settings() -: _sendFilesWay(SendFilesWay::Album) +: _sendFilesWay(Ui::SendFilesWay::Album) , _sendSubmitWay(Ui::InputSubmitSettings::Enter) , _floatPlayerColumn(Window::Column::Second) , _floatPlayerCorner(RectPart::TopRight) @@ -309,11 +310,11 @@ void Settings::addFromSerialized(const QByteArray &serialized) { _callAudioDuckingEnabled = (callAudioDuckingEnabled == 1); _lastSeenWarningSeen = (lastSeenWarningSeen == 1); _soundOverrides = std::move(soundOverrides); - auto uncheckedSendFilesWay = static_cast(sendFilesWay); + auto uncheckedSendFilesWay = static_cast(sendFilesWay); switch (uncheckedSendFilesWay) { - case SendFilesWay::Album: - case SendFilesWay::Photos: - case SendFilesWay::Files: _sendFilesWay = uncheckedSendFilesWay; break; + case Ui::SendFilesWay::Album: + case Ui::SendFilesWay::Photos: + case Ui::SendFilesWay::Files: _sendFilesWay = uncheckedSendFilesWay; break; } auto uncheckedSendSubmitWay = static_cast(sendSubmitWay); switch (uncheckedSendSubmitWay) { @@ -469,7 +470,7 @@ void Settings::resetOnLastLogout() { //_themesAccentColors = Window::Theme::AccentColors(); _lastSeenWarningSeen = false; - _sendFilesWay = SendFilesWay::Album; + _sendFilesWay = Ui::SendFilesWay::Album; //_sendSubmitWay = Ui::InputSubmitSettings::Enter; _soundOverrides = {}; diff --git a/Telegram/SourceFiles/core/core_settings.h b/Telegram/SourceFiles/core/core_settings.h index d9c0bfaa8..bab3bd920 100644 --- a/Telegram/SourceFiles/core/core_settings.h +++ b/Telegram/SourceFiles/core/core_settings.h @@ -10,11 +10,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "window/themes/window_themes_embedded.h" #include "window/window_controls_layout.h" -enum class SendFilesWay; enum class RectPart; namespace Ui { enum class InputSubmitSettings; +enum class SendFilesWay; } // namespace Ui namespace Window { @@ -229,10 +229,10 @@ public: [[nodiscard]] bool lastSeenWarningSeen() const { return _lastSeenWarningSeen; } - void setSendFilesWay(SendFilesWay way) { + void setSendFilesWay(Ui::SendFilesWay way) { _sendFilesWay = way; } - [[nodiscard]] SendFilesWay sendFilesWay() const { + [[nodiscard]] Ui::SendFilesWay sendFilesWay() const { return _sendFilesWay; } void setSendSubmitWay(Ui::InputSubmitSettings value) { @@ -515,7 +515,7 @@ private: bool _callAudioDuckingEnabled = true; Window::Theme::AccentColors _themesAccentColors; bool _lastSeenWarningSeen = false; - SendFilesWay _sendFilesWay; + Ui::SendFilesWay _sendFilesWay; Ui::InputSubmitSettings _sendSubmitWay; base::flat_map _soundOverrides; bool _exeLaunchWarning = true; diff --git a/Telegram/SourceFiles/core/mime_type.cpp b/Telegram/SourceFiles/core/mime_type.cpp index c1596e550..71e7ae065 100644 --- a/Telegram/SourceFiles/core/mime_type.cpp +++ b/Telegram/SourceFiles/core/mime_type.cpp @@ -118,4 +118,23 @@ bool IsMimeAcceptedForAlbum(const QString &mime) { || (mime == u"video/quicktime"_q); } +bool FileIsImage(const QString &name, const QString &mime) { + QString lowermime = mime.toLower(), namelower = name.toLower(); + if (lowermime.startsWith(qstr("image/"))) { + return true; + } else if (namelower.endsWith(qstr(".bmp")) + || namelower.endsWith(qstr(".jpg")) + || namelower.endsWith(qstr(".jpeg")) + || namelower.endsWith(qstr(".gif")) + || namelower.endsWith(qstr(".webp")) + || namelower.endsWith(qstr(".tga")) + || namelower.endsWith(qstr(".tiff")) + || namelower.endsWith(qstr(".tif")) + || namelower.endsWith(qstr(".psd")) + || namelower.endsWith(qstr(".png"))) { + return true; + } + return false; +} + } // namespace Core diff --git a/Telegram/SourceFiles/core/mime_type.h b/Telegram/SourceFiles/core/mime_type.h index b817305f1..73b2dcc9b 100644 --- a/Telegram/SourceFiles/core/mime_type.h +++ b/Telegram/SourceFiles/core/mime_type.h @@ -36,12 +36,14 @@ private: }; -MimeType MimeTypeForName(const QString &mime); -MimeType MimeTypeForFile(const QFileInfo &file); -MimeType MimeTypeForData(const QByteArray &data); +[[nodiscard]] MimeType MimeTypeForName(const QString &mime); +[[nodiscard]] MimeType MimeTypeForFile(const QFileInfo &file); +[[nodiscard]] MimeType MimeTypeForData(const QByteArray &data); -bool IsMimeStickerAnimated(const QString &mime); -bool IsMimeSticker(const QString &mime); -bool IsMimeAcceptedForAlbum(const QString &mime); +[[nodiscard]] bool IsMimeStickerAnimated(const QString &mime); +[[nodiscard]] bool IsMimeSticker(const QString &mime); +[[nodiscard]] bool IsMimeAcceptedForAlbum(const QString &mime); + +[[nodiscard]] bool FileIsImage(const QString &name, const QString &mime); } // namespace Core diff --git a/Telegram/SourceFiles/data/data_document.cpp b/Telegram/SourceFiles/data/data_document.cpp index 38a511a8e..9d9dbf3e1 100644 --- a/Telegram/SourceFiles/data/data_document.cpp +++ b/Telegram/SourceFiles/data/data_document.cpp @@ -39,6 +39,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "boxes/confirm_box.h" #include "ui/image/image.h" #include "ui/text/text_utilities.h" +#include "ui/text/format_values.h" #include "base/base_file_utilities.h" #include "mainwindow.h" #include "core/application.h" @@ -125,25 +126,6 @@ void LaunchWithWarning( } // namespace -bool fileIsImage(const QString &name, const QString &mime) { - QString lowermime = mime.toLower(), namelower = name.toLower(); - if (lowermime.startsWith(qstr("image/"))) { - return true; - } else if (namelower.endsWith(qstr(".bmp")) - || namelower.endsWith(qstr(".jpg")) - || namelower.endsWith(qstr(".jpeg")) - || namelower.endsWith(qstr(".gif")) - || namelower.endsWith(qstr(".webp")) - || namelower.endsWith(qstr(".tga")) - || namelower.endsWith(qstr(".tiff")) - || namelower.endsWith(qstr(".tif")) - || namelower.endsWith(qstr(".psd")) - || namelower.endsWith(qstr(".png"))) { - return true; - } - return false; -} - QString FileNameUnsafe( not_null session, const QString &title, @@ -1456,12 +1438,12 @@ uint8 DocumentData::cacheTag() const { QString DocumentData::composeNameString() const { if (auto songData = song()) { - return ComposeNameString( + return Ui::ComposeNameString( _filename, songData->title, songData->performer); } - return ComposeNameString(_filename, QString(), QString()); + return Ui::ComposeNameString(_filename, QString(), QString()); } LocationType DocumentData::locationType() const { @@ -1577,7 +1559,7 @@ void DocumentData::setMaybeSupportsStreaming(bool supports) { void DocumentData::recountIsImage() { const auto isImage = !isAnimation() && !isVideoFile() - && fileIsImage(filename(), mimeString()); + && Core::FileIsImage(filename(), mimeString()); if (isImage) { _flags |= Flag::ImageType; } else { @@ -1635,22 +1617,6 @@ void DocumentData::collectLocalData(not_null local) { } } -QString DocumentData::ComposeNameString( - const QString &filename, - const QString &songTitle, - const QString &songPerformer) { - if (songTitle.isEmpty() && songPerformer.isEmpty()) { - return filename.isEmpty() ? qsl("Unknown File") : filename; - } - - if (songPerformer.isEmpty()) { - return songTitle; - } - - auto trackTitle = (songTitle.isEmpty() ? qsl("Unknown Track") : songTitle); - return songPerformer + QString::fromUtf8(" \xe2\x80\x93 ") + trackTitle; -} - namespace Data { QString FileExtension(const QString &filepath) { diff --git a/Telegram/SourceFiles/data/data_document.h b/Telegram/SourceFiles/data/data_document.h index e8b5b905f..a31dd6370 100644 --- a/Telegram/SourceFiles/data/data_document.h +++ b/Telegram/SourceFiles/data/data_document.h @@ -78,8 +78,6 @@ struct VoiceData : public DocumentAdditionalData { char wavemax = 0; }; -bool fileIsImage(const QString &name, const QString &mime); - namespace Serialize { class Document; } // namespace Serialize; @@ -228,10 +226,6 @@ public: [[nodiscard]] Storage::Cache::Key cacheKey() const; [[nodiscard]] uint8 cacheTag() const; - [[nodiscard]] static QString ComposeNameString( - const QString &filename, - const QString &songTitle, - const QString &songPerformer); [[nodiscard]] QString composeNameString() const; [[nodiscard]] bool canBeStreamed() const; diff --git a/Telegram/SourceFiles/history/history_drag_area.cpp b/Telegram/SourceFiles/history/history_drag_area.cpp index 5614ad254..92a3041a9 100644 --- a/Telegram/SourceFiles/history/history_drag_area.cpp +++ b/Telegram/SourceFiles/history/history_drag_area.cpp @@ -18,10 +18,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "lang/lang_keys.h" #include "ui/widgets/shadow.h" #include "ui/ui_utility.h" +#include "ui/cached_round_corners.h" #include "mainwindow.h" #include "apiwrap.h" #include "mainwidget.h" -#include "app.h" #include "storage/storage_media_prepare.h" #include "styles/style_chat_helpers.h" #include "styles/style_layers.h" @@ -315,7 +315,7 @@ void DragArea::paintEvent(QPaintEvent *e) { } Ui::Shadow::paint(p, inner, width(), st::boxRoundShadow); - App::roundRect(p, inner, st::boxBg, BoxCorners); + Ui::FillRoundRect(p, inner, st::boxBg, Ui::BoxCorners); p.setPen(anim::pen( st::dragColor, diff --git a/Telegram/SourceFiles/history/history_inner_widget.cpp b/Telegram/SourceFiles/history/history_inner_widget.cpp index e44c610b8..6f9db66fd 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.cpp +++ b/Telegram/SourceFiles/history/history_inner_widget.cpp @@ -26,6 +26,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/toast/toast.h" #include "ui/text/text_options.h" #include "ui/ui_utility.h" +#include "ui/cached_round_corners.h" #include "ui/inactive_press.h" #include "window/window_session_controller.h" #include "window/window_peer_menu.h" @@ -591,7 +592,7 @@ void HistoryInner::paintEvent(QPaintEvent *e) { if (!_firstLoading && _botAbout && !_botAbout->info->text.isEmpty() && _botAbout->height > 0) { if (clip.y() < _botAbout->rect.y() + _botAbout->rect.height() && clip.y() + clip.height() > _botAbout->rect.y()) { p.setTextPalette(st::inTextPalette); - App::roundRect(p, _botAbout->rect, st::msgInBg, MessageInCorners, &st::msgInShadow); + Ui::FillRoundRect(p, _botAbout->rect, st::msgInBg, Ui::MessageInCorners, &st::msgInShadow); auto top = _botAbout->rect.top() + st::msgPadding.top(); if (!_history->peer->isRepliesChat()) { diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index 9abb0d823..8bd28b1dc 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -31,6 +31,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/text/text_utilities.h" // Ui::Text::ToUpper #include "ui/text/format_values.h" #include "ui/chat/message_bar.h" +#include "ui/chat/attach/attach_common.h" #include "ui/image/image.h" #include "ui/special_buttons.h" #include "ui/controls/emoji_button.h" @@ -4193,17 +4194,17 @@ bool HistoryWidget::confirmSendingFiles( _field->setTextWithTags({}); box->setConfirmedCallback(crl::guard(this, [=]( Ui::PreparedList &&list, - SendFilesWay way, + Ui::SendFilesWay way, TextWithTags &&caption, Api::SendOptions options, bool ctrlShiftEnter) { if (showSendingFilesError(list)) { return; } - const auto type = (way == SendFilesWay::Files) + const auto type = (way == Ui::SendFilesWay::Files) ? SendMediaType::File : SendMediaType::Photo; - const auto album = (way == SendFilesWay::Album) + const auto album = (way == Ui::SendFilesWay::Album) ? std::make_shared() : nullptr; uploadFilesAfterConfirmation( diff --git a/Telegram/SourceFiles/history/view/history_view_message.cpp b/Telegram/SourceFiles/history/view/history_view_message.cpp index 56d59d5af..1eaf6786a 100644 --- a/Telegram/SourceFiles/history/view/history_view_message.cpp +++ b/Telegram/SourceFiles/history/view/history_view_message.cpp @@ -19,6 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/toast/toast.h" #include "ui/text/text_utilities.h" #include "ui/text/text_entity.h" +#include "ui/cached_round_corners.h" #include "data/data_session.h" #include "data/data_user.h" #include "data/data_channel.h" @@ -30,7 +31,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "apiwrap.h" #include "layout.h" #include "facades.h" -#include "app.h" #include "styles/style_widgets.h" #include "styles/style_chat.h" #include "styles/style_dialogs.h" @@ -81,11 +81,11 @@ void KeyboardStyle::paintButtonBg( Painter &p, const QRect &rect, float64 howMuchOver) const { - App::roundRect(p, rect, st::msgServiceBg, StickerCorners); + Ui::FillRoundRect(p, rect, st::msgServiceBg, Ui::StickerCorners); if (howMuchOver > 0) { auto o = p.opacity(); p.setOpacity(o * howMuchOver); - App::roundRect(p, rect, st::msgBotKbOverBgAdd, BotKbOverCorners); + Ui::FillRoundRect(p, rect, st::msgBotKbOverBgAdd, Ui::BotKbOverCorners); p.setOpacity(o); } } @@ -141,7 +141,7 @@ QString FastReplyText() { void PaintBubble(Painter &p, QRect rect, int outerWidth, bool selected, bool outbg, RectPart tailSide, RectParts skip) { auto &bg = selected ? (outbg ? st::msgOutBgSelected : st::msgInBgSelected) : (outbg ? st::msgOutBg : st::msgInBg); auto sh = &(selected ? (outbg ? st::msgOutShadowSelected : st::msgInShadowSelected) : (outbg ? st::msgOutShadow : st::msgInShadow)); - auto cors = selected ? (outbg ? MessageOutSelectedCorners : MessageInSelectedCorners) : (outbg ? MessageOutCorners : MessageInCorners); + auto cors = selected ? (outbg ? Ui::MessageOutSelectedCorners : Ui::MessageInSelectedCorners) : (outbg ? Ui::MessageOutCorners : Ui::MessageInCorners); auto parts = RectPart::None | RectPart::NoTopBottom; if (skip & RectPart::Top) { if (skip & RectPart::Bottom) { @@ -174,7 +174,7 @@ void PaintBubble(Painter &p, QRect rect, int outerWidth, bool selected, bool out } else if (!(skip & RectPart::Bottom)) { parts |= RectPart::FullBottom; } - App::roundRect(p, rect, bg, cors, sh, parts); + Ui::FillRoundRect(p, rect, bg, cors, sh, parts); } void PaintBubble(Painter &p, QRect rect, int outerWidth, bool selected, const std::vector &selection, bool outbg, RectPart tailSide) { @@ -1799,10 +1799,10 @@ void Message::drawInfo( auto dateY = infoBottom - st::msgDateFont->height; if (type == InfoDisplayType::Image) { auto dateW = infoW + 2 * st::msgDateImgPadding.x(), dateH = st::msgDateFont->height + 2 * st::msgDateImgPadding.y(); - App::roundRect(p, dateX - st::msgDateImgPadding.x(), dateY - st::msgDateImgPadding.y(), dateW, dateH, selected ? st::msgDateImgBgSelected : st::msgDateImgBg, selected ? DateSelectedCorners : DateCorners); + Ui::FillRoundRect(p, dateX - st::msgDateImgPadding.x(), dateY - st::msgDateImgPadding.y(), dateW, dateH, selected ? st::msgDateImgBgSelected : st::msgDateImgBg, selected ? Ui::DateSelectedCorners : Ui::DateCorners); } else if (type == InfoDisplayType::Background) { auto dateW = infoW + 2 * st::msgDateImgPadding.x(), dateH = st::msgDateFont->height + 2 * st::msgDateImgPadding.y(); - App::roundRect(p, dateX - st::msgDateImgPadding.x(), dateY - st::msgDateImgPadding.y(), dateW, dateH, selected ? st::msgServiceBgSelected : st::msgServiceBg, selected ? StickerSelectedCorners : StickerCorners); + Ui::FillRoundRect(p, dateX - st::msgDateImgPadding.x(), dateY - st::msgDateImgPadding.y(), dateW, dateH, selected ? st::msgServiceBgSelected : st::msgServiceBg, selected ? Ui::StickerSelectedCorners : Ui::StickerCorners); } dateX += timeLeft(); diff --git a/Telegram/SourceFiles/history/view/history_view_replies_section.cpp b/Telegram/SourceFiles/history/view/history_view_replies_section.cpp index 613acdc9d..37d256a6b 100644 --- a/Telegram/SourceFiles/history/view/history_view_replies_section.cpp +++ b/Telegram/SourceFiles/history/view/history_view_replies_section.cpp @@ -27,6 +27,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/text/format_values.h" #include "ui/text/text_utilities.h" #include "ui/chat/attach/attach_prepare.h" +#include "ui/chat/attach/attach_common.h" #include "ui/special_buttons.h" #include "ui/ui_utility.h" #include "ui/toasts/common_toasts.h" @@ -635,17 +636,17 @@ bool RepliesWidget::confirmSendingFiles( const auto replyTo = replyToId(); box->setConfirmedCallback(crl::guard(this, [=]( Ui::PreparedList &&list, - SendFilesWay way, + Ui::SendFilesWay way, TextWithTags &&caption, Api::SendOptions options, bool ctrlShiftEnter) { if (showSendingFilesError(list)) { return; } - const auto type = (way == SendFilesWay::Files) + const auto type = (way == Ui::SendFilesWay::Files) ? SendMediaType::File : SendMediaType::Photo; - const auto album = (way == SendFilesWay::Album) + const auto album = (way == Ui::SendFilesWay::Album) ? std::make_shared() : nullptr; uploadFilesAfterConfirmation( diff --git a/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp b/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp index ad47b2da3..94015efa2 100644 --- a/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp +++ b/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp @@ -21,6 +21,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/item_text_options.h" #include "ui/toast/toast.h" #include "ui/chat/attach/attach_prepare.h" +#include "ui/chat/attach/attach_common.h" #include "ui/special_buttons.h" #include "ui/ui_utility.h" #include "ui/toasts/common_toasts.h" @@ -376,17 +377,17 @@ bool ScheduledWidget::confirmSendingFiles( box->setConfirmedCallback(crl::guard(this, [=]( Ui::PreparedList &&list, - SendFilesWay way, + Ui::SendFilesWay way, TextWithTags &&caption, Api::SendOptions options, bool ctrlShiftEnter) { if (showSendingFilesError(list)) { return; } - const auto type = (way == SendFilesWay::Files) + const auto type = (way == Ui::SendFilesWay::Files) ? SendMediaType::File : SendMediaType::Photo; - const auto album = (way == SendFilesWay::Album) + const auto album = (way == Ui::SendFilesWay::Album) ? std::make_shared() : nullptr; uploadFilesAfterConfirmation( diff --git a/Telegram/SourceFiles/history/view/media/history_view_document.cpp b/Telegram/SourceFiles/history/view/media/history_view_document.cpp index 6eb21fba3..0aa633d6e 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_document.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_document.cpp @@ -18,13 +18,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/view/media/history_view_media_common.h" #include "ui/image/image.h" #include "ui/text/format_values.h" +#include "ui/cached_round_corners.h" #include "layout.h" // FullSelection #include "data/data_session.h" #include "data/data_document.h" #include "data/data_document_media.h" #include "data/data_media_types.h" #include "data/data_file_origin.h" -#include "app.h" #include "styles/style_chat.h" namespace HistoryView { @@ -306,8 +306,8 @@ void Document::draw( } p.drawPixmap(rthumb.topLeft(), thumb); if (selected) { - auto overlayCorners = inWebPage ? SelectedOverlaySmallCorners : SelectedOverlayLargeCorners; - App::roundRect(p, rthumb, p.textPalette().selectOverlay, overlayCorners); + auto overlayCorners = inWebPage ? Ui::SelectedOverlaySmallCorners : Ui::SelectedOverlayLargeCorners; + Ui::FillRoundRect(p, rthumb, p.textPalette().selectOverlay, overlayCorners); } if (radial || (!loaded && !_data->loading())) { diff --git a/Telegram/SourceFiles/history/view/media/history_view_game.cpp b/Telegram/SourceFiles/history/view/media/history_view_game.cpp index 133c008f3..af6f8a282 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_game.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_game.cpp @@ -15,11 +15,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/view/history_view_cursor_state.h" #include "history/view/media/history_view_media_common.h" #include "ui/item_text_options.h" +#include "ui/cached_round_corners.h" #include "core/ui_integration.h" #include "data/data_session.h" #include "data/data_game.h" #include "data/data_media_types.h" -#include "app.h" #include "styles/style_chat.h" namespace HistoryView { @@ -261,7 +261,7 @@ void Game::draw(Painter &p, const QRect &r, TextSelection selection, crl::time m auto gameX = pixwidth - st::msgDateImgDelta - gameW; auto gameY = pixheight - st::msgDateImgDelta - gameH; - App::roundRect(p, style::rtlrect(gameX, gameY, gameW, gameH, pixwidth), selected ? st::msgDateImgBgSelected : st::msgDateImgBg, selected ? DateSelectedCorners : DateCorners); + Ui::FillRoundRect(p, style::rtlrect(gameX, gameY, gameW, gameH, pixwidth), selected ? st::msgDateImgBgSelected : st::msgDateImgBg, selected ? Ui::DateSelectedCorners : Ui::DateCorners); p.setFont(st::msgDateFont); p.setPen(st::msgDateImgFg); diff --git a/Telegram/SourceFiles/history/view/media/history_view_gif.cpp b/Telegram/SourceFiles/history/view/media/history_view_gif.cpp index 3fab69882..43916d11d 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_gif.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_gif.cpp @@ -29,12 +29,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/image/image.h" #include "ui/text/format_values.h" #include "ui/grouped_layout.h" +#include "ui/cached_round_corners.h" #include "data/data_session.h" #include "data/data_streaming.h" #include "data/data_document.h" #include "data/data_file_origin.h" #include "data/data_document_media.h" -#include "app.h" #include "layout.h" // FullSelection #include "styles/style_chat.h" @@ -343,7 +343,7 @@ void Gif::draw(Painter &p, const QRect &r, TextSelection selection, crl::time ms } } } else if (!isRound) { - App::roundShadow(p, 0, 0, paintw, height(), selected ? st::msgInShadowSelected : st::msgInShadow, selected ? InSelectedShadowCorners : InShadowCorners); + Ui::FillRoundShadow(p, 0, 0, paintw, height(), selected ? st::msgInShadowSelected : st::msgInShadow, selected ? Ui::InSelectedShadowCorners : Ui::InShadowCorners); } auto usex = 0, usew = paintw; @@ -451,14 +451,14 @@ void Gif::draw(Painter &p, const QRect &r, TextSelection selection, crl::time ms | RectPart::NoTopBottom | (roundTop ? RectPart::Top : RectPart::None) | (roundBottom ? RectPart::Bottom : RectPart::None); - App::roundRect(p, rthumb.marginsAdded({ 0, roundTop ? 0 : margin, 0, roundBottom ? 0 : margin }), st::imageBg, roundRadius, parts); + Ui::FillRoundRect(p, rthumb.marginsAdded({ 0, roundTop ? 0 : margin, 0, roundBottom ? 0 : margin }), st::imageBg, roundRadius, parts); } } } } if (selected) { - App::complexOverlayRect(p, rthumb, roundRadius, roundCorners); + Ui::FillComplexOverlayRect(p, rthumb, roundRadius, roundCorners); } if (radial @@ -553,7 +553,7 @@ void Gif::draw(Painter &p, const QRect &r, TextSelection selection, crl::time ms if (mediaUnread) { statusW += st::mediaUnreadSkip + st::mediaUnreadSize; } - App::roundRect(p, style::rtlrect(statusX - st::msgDateImgPadding.x(), statusY - st::msgDateImgPadding.y(), statusW, statusH, width()), selected ? st::msgServiceBgSelected : st::msgServiceBg, selected ? StickerSelectedCorners : StickerCorners); + Ui::FillRoundRect(p, style::rtlrect(statusX - st::msgDateImgPadding.x(), statusY - st::msgDateImgPadding.y(), statusW, statusH, width()), selected ? st::msgServiceBgSelected : st::msgServiceBg, selected ? Ui::StickerSelectedCorners : Ui::StickerCorners); p.setFont(st::normalFont); p.setPen(st::msgServiceFg); p.drawTextLeft(statusX, statusY, width(), _statusText, statusW - 2 * st::msgDateImgPadding.x()); @@ -584,7 +584,7 @@ void Gif::draw(Painter &p, const QRect &r, TextSelection selection, crl::time ms int recty = painty; if (rtl()) rectx = width() - rectx - rectw; - App::roundRect(p, rectx, recty, rectw, recth, selected ? st::msgServiceBgSelected : st::msgServiceBg, selected ? StickerSelectedCorners : StickerCorners); + Ui::FillRoundRect(p, rectx, recty, rectw, recth, selected ? st::msgServiceBgSelected : st::msgServiceBg, selected ? Ui::StickerSelectedCorners : Ui::StickerCorners); p.setPen(st::msgServiceFg); rectx += st::msgReplyPadding.left(); rectw = innerw; @@ -677,7 +677,7 @@ void Gif::drawCornerStatus(Painter &p, bool selected, QPoint position) const { const auto statusY = position.y() + st::msgDateImgDelta + padding.y(); const auto around = style::rtlrect(statusX - padding.x(), statusY - padding.y(), statusW, statusH, width()); const auto statusTextTop = statusY + (cornerDownload ? (((statusH - 2 * st::normalFont->height) / 3) - padding.y()) : 0); - App::roundRect(p, around, selected ? st::msgDateImgBgSelected : st::msgDateImgBg, selected ? DateSelectedCorners : DateCorners); + Ui::FillRoundRect(p, around, selected ? st::msgDateImgBgSelected : st::msgDateImgBg, selected ? Ui::DateSelectedCorners : Ui::DateCorners); p.setFont(st::normalFont); p.setPen(st::msgDateImgFg); p.drawTextLeft(statusX + addLeft, statusTextTop, width(), text, statusW - 2 * padding.x()); @@ -985,7 +985,7 @@ void Gif::drawGrouped( } if (selected) { - App::complexOverlayRect(p, geometry, roundRadius, corners); + Ui::FillComplexOverlayRect(p, geometry, roundRadius, corners); } if (radial diff --git a/Telegram/SourceFiles/history/view/media/history_view_invoice.cpp b/Telegram/SourceFiles/history/view/media/history_view_invoice.cpp index 5e3549655..dbd40997b 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_invoice.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_invoice.cpp @@ -15,8 +15,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/view/media/history_view_media_common.h" #include "ui/item_text_options.h" #include "ui/text/format_values.h" +#include "ui/cached_round_corners.h" #include "data/data_media_types.h" -#include "app.h" #include "styles/style_chat.h" namespace HistoryView { @@ -260,7 +260,7 @@ void Invoice::draw(Painter &p, const QRect &r, TextSelection selection, crl::tim auto statusX = st::msgDateImgDelta; auto statusY = st::msgDateImgDelta; - App::roundRect(p, style::rtlrect(statusX, statusY, statusW, statusH, pixwidth), selected ? st::msgDateImgBgSelected : st::msgDateImgBg, selected ? DateSelectedCorners : DateCorners); + Ui::FillRoundRect(p, style::rtlrect(statusX, statusY, statusW, statusH, pixwidth), selected ? st::msgDateImgBgSelected : st::msgDateImgBg, selected ? Ui::DateSelectedCorners : Ui::DateCorners); p.setFont(st::msgDateFont); p.setPen(st::msgDateImgFg); diff --git a/Telegram/SourceFiles/history/view/media/history_view_location.cpp b/Telegram/SourceFiles/history/view/media/history_view_location.cpp index 70a1bfbd7..f539dddcb 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_location.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_location.cpp @@ -16,10 +16,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/view/history_view_cursor_state.h" #include "ui/image/image.h" #include "ui/text/text_options.h" +#include "ui/cached_round_corners.h" #include "data/data_session.h" #include "data/data_file_origin.h" #include "data/data_cloud_file.h" -#include "app.h" #include "styles/style_chat.h" namespace HistoryView { @@ -179,7 +179,7 @@ void Location::draw(Painter &p, const QRect &r, TextSelection selection, crl::ti } painth -= painty; } else { - App::roundShadow(p, 0, 0, paintw, painth, selected ? st::msgInShadowSelected : st::msgInShadow, selected ? InSelectedShadowCorners : InShadowCorners); + Ui::FillRoundShadow(p, 0, 0, paintw, painth, selected ? st::msgInShadowSelected : st::msgInShadow, selected ? Ui::InSelectedShadowCorners : Ui::InShadowCorners); } auto roundRadius = ImageRoundRadius::Large; @@ -191,7 +191,7 @@ void Location::draw(Painter &p, const QRect &r, TextSelection selection, crl::ti const auto &pix = thumbnail->pixSingle(paintw, painth, paintw, painth, roundRadius, roundCorners); p.drawPixmap(rthumb.topLeft(), pix); } else { - App::complexLocationRect(p, rthumb, roundRadius, roundCorners); + Ui::FillComplexLocationRect(p, rthumb, roundRadius, roundCorners); } const auto paintMarker = [&](const style::icon &icon) { icon.paint( @@ -203,7 +203,7 @@ void Location::draw(Painter &p, const QRect &r, TextSelection selection, crl::ti paintMarker(st::historyMapPoint); paintMarker(st::historyMapPointInner); if (selected) { - App::complexOverlayRect(p, rthumb, roundRadius, roundCorners); + Ui::FillComplexOverlayRect(p, rthumb, roundRadius, roundCorners); } if (_parent->media() == this) { diff --git a/Telegram/SourceFiles/history/view/media/history_view_media_unwrapped.cpp b/Telegram/SourceFiles/history/view/media/history_view_media_unwrapped.cpp index 9345ad268..8b2b63b97 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_media_unwrapped.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_media_unwrapped.cpp @@ -15,8 +15,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "lottie/lottie_single_player.h" #include "core/application.h" #include "core/core_settings.h" +#include "ui/cached_round_corners.h" #include "layout.h" -#include "app.h" #include "styles/style_chat.h" namespace HistoryView { @@ -206,7 +206,7 @@ void UnwrappedMedia::drawSurrounding( int recty = 0; if (rtl()) rectx = width() - rectx - rectw; - App::roundRect(p, rectx, recty, rectw, recth, selected ? st::msgServiceBgSelected : st::msgServiceBg, selected ? StickerSelectedCorners : StickerCorners); + Ui::FillRoundRect(p, rectx, recty, rectw, recth, selected ? st::msgServiceBgSelected : st::msgServiceBg, selected ? Ui::StickerSelectedCorners : Ui::StickerCorners); p.setPen(st::msgServiceFg); rectx += st::msgReplyPadding.left(); rectw -= st::msgReplyPadding.left() + st::msgReplyPadding.right(); diff --git a/Telegram/SourceFiles/history/view/media/history_view_photo.cpp b/Telegram/SourceFiles/history/view/media/history_view_photo.cpp index 193455a9e..87f7e7bf9 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_photo.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_photo.cpp @@ -21,6 +21,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "main/main_session_settings.h" #include "ui/image/image.h" #include "ui/grouped_layout.h" +#include "ui/cached_round_corners.h" #include "data/data_session.h" #include "data/data_streaming.h" #include "data/data_photo.h" @@ -28,7 +29,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_file_origin.h" #include "data/data_auto_download.h" #include "core/application.h" -#include "app.h" #include "styles/style_chat.h" namespace HistoryView { @@ -249,7 +249,7 @@ void Photo::draw(Painter &p, const QRect &r, TextSelection selection, crl::time rthumb = style::rtlrect(paintx, painty, paintw, painth, width()); } } else { - App::roundShadow(p, 0, 0, paintw, painth, selected ? st::msgInShadowSelected : st::msgInShadow, selected ? InSelectedShadowCorners : InShadowCorners); + Ui::FillRoundShadow(p, 0, 0, paintw, painth, selected ? st::msgInShadowSelected : st::msgInShadow, selected ? Ui::InSelectedShadowCorners : Ui::InShadowCorners); } auto inWebPage = (_parent->media() != this); auto roundRadius = inWebPage ? ImageRoundRadius::Small : ImageRoundRadius::Large; @@ -272,7 +272,7 @@ void Photo::draw(Painter &p, const QRect &r, TextSelection selection, crl::time }(); p.drawPixmap(rthumb.topLeft(), pix); if (selected) { - App::complexOverlayRect(p, rthumb, roundRadius, roundCorners); + Ui::FillComplexOverlayRect(p, rthumb, roundRadius, roundCorners); } } if (radial || (!loaded && !_data->loading())) { @@ -505,7 +505,7 @@ void Photo::drawGrouped( p.drawPixmap(geometry.topLeft(), *cache); if (selected) { const auto roundRadius = ImageRoundRadius::Large; - App::complexOverlayRect(p, geometry, roundRadius, corners); + Ui::FillComplexOverlayRect(p, geometry, roundRadius, corners); } const auto displayState = radial diff --git a/Telegram/SourceFiles/history/view/media/history_view_theme_document.cpp b/Telegram/SourceFiles/history/view/media/history_view_theme_document.cpp index 437ab56c0..f6055a7fb 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_theme_document.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_theme_document.cpp @@ -17,9 +17,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_file_origin.h" #include "base/qthelp_url.h" #include "ui/text/format_values.h" +#include "ui/cached_round_corners.h" #include "window/themes/window_theme.h" #include "layout.h" // FullSelection -#include "app.h" +#include "app.h" // App::pixmapFromImageInPlace. #include "styles/style_chat.h" namespace HistoryView { @@ -139,14 +140,14 @@ void ThemeDocument::draw(Painter &p, const QRect &r, TextSelection selection, cr validateThumbnail(); p.drawPixmap(rthumb.topLeft(), _thumbnail); if (selected) { - App::complexOverlayRect(p, rthumb, roundRadius, roundCorners); + Ui::FillComplexOverlayRect(p, rthumb, roundRadius, roundCorners); } auto statusX = paintx + st::msgDateImgDelta + st::msgDateImgPadding.x(); auto statusY = painty + st::msgDateImgDelta + st::msgDateImgPadding.y(); auto statusW = st::normalFont->width(_statusText) + 2 * st::msgDateImgPadding.x(); auto statusH = st::normalFont->height + 2 * st::msgDateImgPadding.y(); - App::roundRect(p, style::rtlrect(statusX - st::msgDateImgPadding.x(), statusY - st::msgDateImgPadding.y(), statusW, statusH, width()), selected ? st::msgDateImgBgSelected : st::msgDateImgBg, selected ? DateSelectedCorners : DateCorners); + Ui::FillRoundRect(p, style::rtlrect(statusX - st::msgDateImgPadding.x(), statusY - st::msgDateImgPadding.y(), statusW, statusH, width()), selected ? st::msgDateImgBgSelected : st::msgDateImgBg, selected ? Ui::DateSelectedCorners : Ui::DateCorners); p.setFont(st::normalFont); p.setPen(st::msgDateImgFg); p.drawTextLeft(statusX, statusY, width(), _statusText, statusW - 2 * st::msgDateImgPadding.x()); diff --git a/Telegram/SourceFiles/history/view/media/history_view_web_page.cpp b/Telegram/SourceFiles/history/view/media/history_view_web_page.cpp index 4e4e395f3..52a6a76ed 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_web_page.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_web_page.cpp @@ -19,6 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/image/image.h" #include "ui/text/text_options.h" #include "ui/text/format_values.h" +#include "ui/cached_round_corners.h" #include "layout.h" // FullSelection #include "data/data_session.h" #include "data/data_media_types.h" @@ -26,7 +27,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_photo.h" #include "data/data_photo_media.h" #include "data/data_file_origin.h" -#include "app.h" #include "styles/style_chat.h" namespace HistoryView { @@ -501,7 +501,7 @@ void WebPage::draw(Painter &p, const QRect &r, TextSelection selection, crl::tim } p.drawPixmapLeft(padding.left() + paintw - pw, tshift, width(), pix); if (selected) { - App::roundRect(p, style::rtlrect(padding.left() + paintw - pw, tshift, pw, _pixh, width()), p.textPalette().selectOverlay, SelectedOverlaySmallCorners); + Ui::FillRoundRect(p, style::rtlrect(padding.left() + paintw - pw, tshift, pw, _pixh, width()), p.textPalette().selectOverlay, Ui::SelectedOverlaySmallCorners); } paintw -= pw + st::webPagePhotoDelta; } @@ -569,7 +569,7 @@ void WebPage::draw(Painter &p, const QRect &r, TextSelection selection, crl::tim auto dateW = pixwidth - dateX - st::msgDateImgDelta; auto dateH = pixheight - dateY - st::msgDateImgDelta; - App::roundRect(p, dateX, dateY, dateW, dateH, selected ? st::msgDateImgBgSelected : st::msgDateImgBg, selected ? DateSelectedCorners : DateCorners); + Ui::FillRoundRect(p, dateX, dateY, dateW, dateH, selected ? st::msgDateImgBgSelected : st::msgDateImgBg, selected ? Ui::DateSelectedCorners : Ui::DateCorners); p.setFont(st::msgDateFont); p.setPen(st::msgDateImgFg); diff --git a/Telegram/SourceFiles/info/info_layer_widget.cpp b/Telegram/SourceFiles/info/info_layer_widget.cpp index cd062dbc7..1627b1383 100644 --- a/Telegram/SourceFiles/info/info_layer_widget.cpp +++ b/Telegram/SourceFiles/info/info_layer_widget.cpp @@ -13,13 +13,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/rp_widget.h" #include "ui/focus_persister.h" #include "ui/widgets/buttons.h" +#include "ui/cached_round_corners.h" #include "window/section_widget.h" #include "window/window_session_controller.h" #include "window/main_window.h" #include "main/main_session.h" #include "boxes/abstract_box.h" #include "core/application.h" -#include "app.h" +#include "app.h" // App::quitting. #include "styles/style_info.h" #include "styles/style_window.h" #include "styles/style_layers.h" @@ -269,11 +270,11 @@ void LayerWidget::paintEvent(QPaintEvent *e) { } } if (parts) { - App::roundRect( + Ui::FillRoundRect( p, rect(), st::boxBg, - BoxCorners, + Ui::BoxCorners, nullptr, parts); } diff --git a/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp b/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp index dde873ad1..4810cbc4a 100644 --- a/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp +++ b/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp @@ -25,9 +25,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/view/history_view_cursor_state.h" #include "ui/image/image.h" #include "ui/text/format_values.h" +#include "ui/cached_round_corners.h" #include "main/main_session.h" #include "lang/lang_keys.h" -#include "app.h" #include "styles/style_overview.h" #include "styles/style_chat.h" #include "styles/style_chat_helpers.h" @@ -460,7 +460,7 @@ void Sticker::paint(Painter &p, const QRect &clip, const PaintContext *context) auto over = _a_over.value(_active ? 1. : 0.); if (over > 0) { p.setOpacity(over); - App::roundRect(p, QRect(QPoint(0, 0), st::stickerPanSize), st::emojiPanHover, StickerHoverCorners); + Ui::FillRoundRect(p, QRect(QPoint(0, 0), st::stickerPanSize), st::emojiPanHover, Ui::StickerHoverCorners); p.setOpacity(1); } @@ -734,7 +734,7 @@ void Video::paint(Painter &p, const QRect &clip, const PaintContext *context) co int durationTop = st::inlineRowMargin + st::inlineThumbSize - st::normalFont->height - st::inlineDurationMargin; int durationW = _durationWidth + 2 * st::msgDateImgPadding.x(), durationH = st::normalFont->height + 2 * st::msgDateImgPadding.y(); int durationX = (st::inlineThumbSize - durationW) / 2, durationY = st::inlineRowMargin + st::inlineThumbSize - durationH; - App::roundRect(p, durationX, durationY - st::msgDateImgPadding.y(), durationW, durationH, st::msgDateImgBg, DateCorners); + Ui::FillRoundRect(p, durationX, durationY - st::msgDateImgPadding.y(), durationW, durationH, st::msgDateImgBg, Ui::DateCorners); p.setPen(st::msgDateImgFg); p.setFont(st::normalFont); p.drawText(durationX + st::msgDateImgPadding.x(), durationTop + st::normalFont->ascent, _duration); diff --git a/Telegram/SourceFiles/inline_bots/inline_results_widget.cpp b/Telegram/SourceFiles/inline_bots/inline_results_widget.cpp index 335ca6cd5..c3184fbb9 100644 --- a/Telegram/SourceFiles/inline_bots/inline_results_widget.cpp +++ b/Telegram/SourceFiles/inline_bots/inline_results_widget.cpp @@ -33,9 +33,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "window/window_session_controller.h" #include "ui/widgets/scroll_area.h" #include "ui/widgets/labels.h" +#include "ui/cached_round_corners.h" #include "history/view/history_view_cursor_state.h" #include "facades.h" -#include "app.h" #include "styles/style_chat_helpers.h" #include @@ -905,7 +905,7 @@ void Widget::paintEvent(QPaintEvent *e) { void Widget::paintContent(Painter &p) { auto inner = innerRect(); - App::roundRect(p, inner, st::emojiPanBg, ImageRoundRadius::Small, RectPart::FullTop | RectPart::FullBottom); + Ui::FillRoundRect(p, inner, st::emojiPanBg, ImageRoundRadius::Small, RectPart::FullTop | RectPart::FullBottom); auto horizontal = horizontalRect(); auto sidesTop = horizontal.y(); diff --git a/Telegram/SourceFiles/layout.cpp b/Telegram/SourceFiles/layout.cpp index cea341830..6dcba44ab 100644 --- a/Telegram/SourceFiles/layout.cpp +++ b/Telegram/SourceFiles/layout.cpp @@ -18,7 +18,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "media/audio/media_audio.h" #include "storage/localstorage.h" #include "history/view/history_view_cursor_state.h" -#include "app.h" +#include "ui/cached_round_corners.h" int32 documentColorIndex(DocumentData *document, QString &ext) { auto colorIndex = 0; @@ -118,8 +118,8 @@ style::color documentSelectedColor(int32 colorIndex) { return colors[colorIndex & 3]; } -RoundCorners documentCorners(int32 colorIndex) { - return RoundCorners(Doc1Corners + (colorIndex & 3)); +Ui::CachedRoundCorners documentCorners(int32 colorIndex) { + return Ui::CachedRoundCorners(Ui::Doc1Corners + (colorIndex & 3)); } [[nodiscard]] HistoryView::TextState LayoutItemBase::getState( diff --git a/Telegram/SourceFiles/layout.h b/Telegram/SourceFiles/layout.h index 7f5033db3..8fd524927 100644 --- a/Telegram/SourceFiles/layout.h +++ b/Telegram/SourceFiles/layout.h @@ -14,7 +14,9 @@ struct TextState; struct StateRequest; } // namespace HistoryView -enum RoundCorners : int; +namespace Ui { +enum CachedRoundCorners : int; +} // namespace Ui constexpr auto FullSelection = TextSelection { 0xFFFF, 0xFFFF }; @@ -57,7 +59,7 @@ style::color documentColor(int colorIndex); style::color documentDarkColor(int colorIndex); style::color documentOverColor(int colorIndex); style::color documentSelectedColor(int colorIndex); -RoundCorners documentCorners(int colorIndex); +Ui::CachedRoundCorners documentCorners(int colorIndex); class PaintContextBase { public: diff --git a/Telegram/SourceFiles/main/main_session_settings.cpp b/Telegram/SourceFiles/main/main_session_settings.cpp index 4a80fe280..6b178cd01 100644 --- a/Telegram/SourceFiles/main/main_session_settings.cpp +++ b/Telegram/SourceFiles/main/main_session_settings.cpp @@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "chat_helpers/tabbed_selector.h" #include "ui/widgets/input_fields.h" +#include "ui/chat/attach/attach_common.h" #include "window/section_widget.h" #include "support/support_common.h" #include "storage/serialize_common.h" @@ -367,11 +368,11 @@ void SessionSettings::addFromSerialized(const QByteArray &serialized) { for (const auto &[key, value] : appSoundOverrides) { app.setSoundOverride(key, value); } - auto uncheckedSendFilesWay = static_cast(appSendFilesWay); + auto uncheckedSendFilesWay = static_cast(appSendFilesWay); switch (uncheckedSendFilesWay) { - case SendFilesWay::Album: - case SendFilesWay::Photos: - case SendFilesWay::Files: app.setSendFilesWay(uncheckedSendFilesWay); break; + case Ui::SendFilesWay::Album: + case Ui::SendFilesWay::Photos: + case Ui::SendFilesWay::Files: app.setSendFilesWay(uncheckedSendFilesWay); break; } auto uncheckedSendSubmitWay = static_cast( appSendSubmitWay); diff --git a/Telegram/SourceFiles/media/player/media_player_panel.cpp b/Telegram/SourceFiles/media/player/media_player_panel.cpp index 2be6eb4da..fcc7fe218 100644 --- a/Telegram/SourceFiles/media/player/media_player_panel.cpp +++ b/Telegram/SourceFiles/media/player/media_player_panel.cpp @@ -19,9 +19,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/shadow.h" #include "ui/widgets/scroll_area.h" #include "ui/ui_utility.h" +#include "ui/cached_round_corners.h" #include "mainwindow.h" #include "main/main_session.h" -#include "app.h" #include "styles/style_overview.h" #include "styles/style_widgets.h" #include "styles/style_media_player.h" @@ -159,7 +159,7 @@ void Panel::paintEvent(QPaintEvent *e) { | RectPart::Top; Ui::Shadow::paint(p, shadowedRect, width(), st::defaultRoundShadow, shadowedSides); auto parts = RectPart::Full; - App::roundRect(p, shadowedRect, st::menuBg, MenuCorners, nullptr, parts); + Ui::FillRoundRect(p, shadowedRect, st::menuBg, Ui::MenuCorners, nullptr, parts); } void Panel::enterEventHook(QEvent *e) { diff --git a/Telegram/SourceFiles/media/player/media_player_volume_controller.cpp b/Telegram/SourceFiles/media/player/media_player_volume_controller.cpp index d21a1fbdc..2e2c452af 100644 --- a/Telegram/SourceFiles/media/player/media_player_volume_controller.cpp +++ b/Telegram/SourceFiles/media/player/media_player_volume_controller.cpp @@ -12,13 +12,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/shadow.h" #include "ui/widgets/continuous_sliders.h" #include "ui/ui_utility.h" +#include "ui/cached_round_corners.h" #include "base/object_ptr.h" #include "mainwindow.h" #include "main/main_session.h" #include "window/window_session_controller.h" #include "core/application.h" #include "core/core_settings.h" -#include "app.h" #include "styles/style_media_player.h" #include "styles/style_widgets.h" @@ -146,7 +146,7 @@ void VolumeWidget::paintEvent(QPaintEvent *e) { auto shadowedSides = RectPart::Left | RectPart::Right | RectPart::Bottom; Ui::Shadow::paint(p, shadowedRect, width(), st::defaultRoundShadow, shadowedSides); auto parts = RectPart::NoTopBottom | RectPart::FullBottom; - App::roundRect(p, QRect(shadowedRect.x(), -st::buttonRadius, shadowedRect.width(), shadowedRect.y() + shadowedRect.height() + st::buttonRadius), st::menuBg, MenuCorners, nullptr, parts); + Ui::FillRoundRect(p, QRect(shadowedRect.x(), -st::buttonRadius, shadowedRect.width(), shadowedRect.y() + shadowedRect.height() + st::buttonRadius), st::menuBg, Ui::MenuCorners, nullptr, parts); } void VolumeWidget::enterEventHook(QEvent *e) { diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp index 4771cea59..eff607fcb 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp +++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp @@ -25,6 +25,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/text/format_values.h" #include "ui/item_text_options.h" #include "ui/ui_utility.h" +#include "ui/cached_round_corners.h" #include "boxes/confirm_box.h" #include "media/audio/media_audio.h" #include "media/view/media_view_playback_controls.h" @@ -3014,7 +3015,7 @@ void OverlayWidget::paintEvent(QPaintEvent *e) { _saveMsgOpacity.update(qMin(progress, 1.), anim::linear); if (_saveMsgOpacity.current() > 0) { p.setOpacity(_saveMsgOpacity.current()); - App::roundRect(p, _saveMsg, st::mediaviewSaveMsgBg, MediaviewSaveCorners); + Ui::FillRoundRect(p, _saveMsg, st::mediaviewSaveMsgBg, Ui::MediaviewSaveCorners); st::mediaviewSaveMsgCheck.paint(p, _saveMsg.topLeft() + st::mediaviewSaveMsgCheckPos, width()); p.setPen(st::mediaviewSaveMsgFg); diff --git a/Telegram/SourceFiles/media/view/media_view_playback_controls.cpp b/Telegram/SourceFiles/media/view/media_view_playback_controls.cpp index f2ed6ccbd..3b8bb05be 100644 --- a/Telegram/SourceFiles/media/view/media_view_playback_controls.cpp +++ b/Telegram/SourceFiles/media/view/media_view_playback_controls.cpp @@ -15,8 +15,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/buttons.h" #include "ui/widgets/popup_menu.h" #include "ui/text/format_values.h" +#include "ui/cached_round_corners.h" #include "lang/lang_keys.h" -#include "app.h" #include "styles/style_media_view.h" namespace Media { @@ -429,7 +429,7 @@ void PlaybackControls::paintEvent(QPaintEvent *e) { _volumeController->setFadeOpacity(1.); _childrenHidden = false; } - App::roundRect(p, rect(), st::mediaviewSaveMsgBg, MediaviewSaveCorners); + Ui::FillRoundRect(p, rect(), st::mediaviewSaveMsgBg, Ui::MediaviewSaveCorners); } void PlaybackControls::mousePressEvent(QMouseEvent *e) { diff --git a/Telegram/SourceFiles/overview/overview_layout.cpp b/Telegram/SourceFiles/overview/overview_layout.cpp index 5a4096ec1..57d25bdf4 100644 --- a/Telegram/SourceFiles/overview/overview_layout.cpp +++ b/Telegram/SourceFiles/overview/overview_layout.cpp @@ -38,6 +38,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/image/image.h" #include "ui/text/format_values.h" #include "ui/text/text_options.h" +#include "ui/cached_round_corners.h" #include "app.h" namespace Overview { @@ -501,7 +502,7 @@ void Video::paint(Painter &p, const QRect &clip, TextSelection selection, const 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); - App::roundRect(p, statusX - padding.x(), statusY - padding.y(), statusW, statusH, selected ? st::msgDateImgBgSelected : st::msgDateImgBg, selected ? OverviewVideoSelectedCorners : OverviewVideoCorners); + 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); @@ -1658,19 +1659,19 @@ void Link::validateThumbnail() { const auto index = _letter.isEmpty() ? 0 : (_letter[0].unicode() % 4); - const auto fill = [&](style::color color, RoundCorners corners) { + const auto fill = [&](style::color color, Ui::CachedRoundCorners corners) { auto pixRect = QRect( 0, 0, st::linksPhotoSize, st::linksPhotoSize); - App::roundRect(p, pixRect, color, corners); + Ui::FillRoundRect(p, pixRect, color, corners); }; switch (index) { - case 0: fill(st::msgFile1Bg, Doc1Corners); break; - case 1: fill(st::msgFile2Bg, Doc2Corners); break; - case 2: fill(st::msgFile3Bg, Doc3Corners); break; - case 3: fill(st::msgFile4Bg, Doc4Corners); break; + 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()) { diff --git a/Telegram/SourceFiles/settings/settings_intro.cpp b/Telegram/SourceFiles/settings/settings_intro.cpp index 85a323fbf..7f7424433 100644 --- a/Telegram/SourceFiles/settings/settings_intro.cpp +++ b/Telegram/SourceFiles/settings/settings_intro.cpp @@ -18,10 +18,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/labels.h" #include "ui/widgets/buttons.h" #include "ui/widgets/scroll_area.h" +#include "ui/cached_round_corners.h" #include "lang/lang_keys.h" #include "boxes/abstract_box.h" #include "window/window_controller.h" -#include "app.h" #include "styles/style_settings.h" #include "styles/style_layers.h" #include "styles/style_info.h" @@ -524,11 +524,11 @@ void LayerWidget::paintEvent(QPaintEvent *e) { parts |= RectPart::FullBottom; } if (parts) { - App::roundRect( + Ui::FillRoundRect( p, rect(), st::boxBg, - BoxCorners, + Ui::BoxCorners, nullptr, parts); } diff --git a/Telegram/SourceFiles/settings/settings_privacy_controllers.cpp b/Telegram/SourceFiles/settings/settings_privacy_controllers.cpp index 786e1f5f4..466f1bc9a 100644 --- a/Telegram/SourceFiles/settings/settings_privacy_controllers.cpp +++ b/Telegram/SourceFiles/settings/settings_privacy_controllers.cpp @@ -30,6 +30,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/wrap/vertical_layout.h" #include "ui/wrap/slide_wrap.h" #include "ui/image/image_prepare.h" +#include "ui/cached_round_corners.h" #include "window/section_widget.h" #include "window/window_session_controller.h" #include "boxes/peer_list_controllers.h" @@ -859,7 +860,7 @@ void ForwardsPrivacyController::PaintForwardedTooltip( const auto arrowLeft = arrowLeft2; const auto geometry = rect.translated(left, top); - App::roundRect(p, geometry, st::toastBg, ImageRoundRadius::Small); + Ui::FillRoundRect(p, geometry, st::toastBg, ImageRoundRadius::Small); p.setFont(font); p.setPen(st::toastFg); diff --git a/Telegram/SourceFiles/storage/details/storage_settings_scheme.cpp b/Telegram/SourceFiles/storage/details/storage_settings_scheme.cpp index 729181abf..2bed620ff 100644 --- a/Telegram/SourceFiles/storage/details/storage_settings_scheme.cpp +++ b/Telegram/SourceFiles/storage/details/storage_settings_scheme.cpp @@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "mtproto/mtproto_config.h" #include "ui/effects/animation_value.h" #include "ui/widgets/input_fields.h" +#include "ui/chat/attach/attach_common.h" #include "window/themes/window_theme.h" #include "core/update_checker.h" #include "platform/platform_specific.h" @@ -918,8 +919,8 @@ bool ReadSetting( if (!CheckStreamStatus(stream)) return false; Core::App().settings().setSendFilesWay((v == 1) - ? SendFilesWay::Album - : SendFilesWay::Files); + ? Ui::SendFilesWay::Album + : Ui::SendFilesWay::Files); context.legacyRead = true; } break; diff --git a/Telegram/SourceFiles/ui/cached_round_corners.cpp b/Telegram/SourceFiles/ui/cached_round_corners.cpp new file mode 100644 index 000000000..3402363b9 --- /dev/null +++ b/Telegram/SourceFiles/ui/cached_round_corners.cpp @@ -0,0 +1,255 @@ +/* +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 "ui/cached_round_corners.h" + +#include "ui/ui_utility.h" +#include "ui/image/image_prepare.h" +#include "styles/style_chat.h" +#include "styles/style_layers.h" +#include "styles/style_overview.h" +#include "styles/style_media_view.h" +#include "styles/style_chat_helpers.h" + +namespace Ui { +namespace { + +struct CornersPixmaps { + QPixmap p[4]; +}; +std::vector Corners; +base::flat_map CornersMap; +QImage CornersMaskLarge[4], CornersMaskSmall[4]; +rpl::lifetime PaletteChangedLifetime; + +void PrepareCorners(CachedRoundCorners index, int32 radius, const QBrush &brush, const style::color *shadow = nullptr, QImage *cors = nullptr) { + Expects(Corners.size() > index); + + int32 r = radius * style::DevicePixelRatio(), s = st::msgShadow * style::DevicePixelRatio(); + QImage rect(r * 3, r * 3 + (shadow ? s : 0), QImage::Format_ARGB32_Premultiplied), localCors[4]; + { + Painter p(&rect); + PainterHighQualityEnabler hq(p); + + p.setCompositionMode(QPainter::CompositionMode_Source); + p.fillRect(QRect(0, 0, rect.width(), rect.height()), Qt::transparent); + p.setCompositionMode(QPainter::CompositionMode_SourceOver); + p.setPen(Qt::NoPen); + if (shadow) { + p.setBrush((*shadow)->b); + p.drawRoundedRect(0, s, r * 3, r * 3, r, r); + } + p.setBrush(brush); + 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)); + if (index != SmallMaskCorners && index != LargeMaskCorners) { + for (int i = 0; i < 4; ++i) { + Corners[index].p[i] = PixmapFromImage(std::move(cors[i])); + Corners[index].p[i].setDevicePixelRatio(style::DevicePixelRatio()); + } + } +} + +void CreateMaskCorners() { + QImage mask[4]; + PrepareCorners(SmallMaskCorners, st::buttonRadius, QColor(255, 255, 255), nullptr, mask); + for (int i = 0; i < 4; ++i) { + CornersMaskSmall[i] = mask[i].convertToFormat(QImage::Format_ARGB32_Premultiplied); + CornersMaskSmall[i].setDevicePixelRatio(style::DevicePixelRatio()); + } + PrepareCorners(LargeMaskCorners, st::historyMessageRadius, QColor(255, 255, 255), nullptr, mask); + for (int i = 0; i < 4; ++i) { + CornersMaskLarge[i] = mask[i].convertToFormat(QImage::Format_ARGB32_Premultiplied); + CornersMaskLarge[i].setDevicePixelRatio(style::DevicePixelRatio()); + } +} + +void CreatePaletteCorners() { + PrepareCorners(MenuCorners, st::buttonRadius, st::menuBg); + PrepareCorners(BoxCorners, st::boxRadius, st::boxBg); + PrepareCorners(BotKbOverCorners, st::dateRadius, st::msgBotKbOverBgAdd); + PrepareCorners(StickerCorners, st::dateRadius, st::msgServiceBg); + PrepareCorners(StickerSelectedCorners, st::dateRadius, st::msgServiceBgSelected); + PrepareCorners(SelectedOverlaySmallCorners, st::buttonRadius, st::msgSelectOverlay); + PrepareCorners(SelectedOverlayLargeCorners, st::historyMessageRadius, st::msgSelectOverlay); + PrepareCorners(DateCorners, st::dateRadius, st::msgDateImgBg); + PrepareCorners(DateSelectedCorners, st::dateRadius, st::msgDateImgBgSelected); + PrepareCorners(OverviewVideoCorners, st::overviewVideoStatusRadius, st::msgDateImgBg); + PrepareCorners(OverviewVideoSelectedCorners, st::overviewVideoStatusRadius, st::msgDateImgBgSelected); + PrepareCorners(InShadowCorners, st::historyMessageRadius, st::msgInShadow); + PrepareCorners(InSelectedShadowCorners, st::historyMessageRadius, st::msgInShadowSelected); + PrepareCorners(ForwardCorners, st::historyMessageRadius, st::historyForwardChooseBg); + PrepareCorners(MediaviewSaveCorners, st::mediaviewControllerRadius, st::mediaviewSaveMsgBg); + PrepareCorners(EmojiHoverCorners, st::buttonRadius, st::emojiPanHover); + PrepareCorners(StickerHoverCorners, st::buttonRadius, st::emojiPanHover); + PrepareCorners(BotKeyboardCorners, st::buttonRadius, st::botKbBg); + PrepareCorners(PhotoSelectOverlayCorners, st::buttonRadius, st::overviewPhotoSelectOverlay); + + PrepareCorners(Doc1Corners, st::buttonRadius, st::msgFile1Bg); + PrepareCorners(Doc2Corners, st::buttonRadius, st::msgFile2Bg); + PrepareCorners(Doc3Corners, st::buttonRadius, st::msgFile3Bg); + PrepareCorners(Doc4Corners, st::buttonRadius, st::msgFile4Bg); + + PrepareCorners(MessageInCorners, st::historyMessageRadius, st::msgInBg, &st::msgInShadow); + PrepareCorners(MessageInSelectedCorners, st::historyMessageRadius, st::msgInBgSelected, &st::msgInShadowSelected); + PrepareCorners(MessageOutCorners, st::historyMessageRadius, st::msgOutBg, &st::msgOutShadow); + PrepareCorners(MessageOutSelectedCorners, st::historyMessageRadius, st::msgOutBgSelected, &st::msgOutShadowSelected); +} + +} // namespace + +void StartCachedCorners() { + Corners.resize(RoundCornersCount); + CreateMaskCorners(); + CreatePaletteCorners(); + + style::PaletteChanged( + ) | rpl::start_with_next([=] { + CreatePaletteCorners(); + }, PaletteChangedLifetime); +} + +void FinishCachedCorners() { + Corners.clear(); + CornersMap.clear(); + PaletteChangedLifetime.destroy(); +} + +void RectWithCorners(Painter &p, QRect rect, const style::color &bg, CachedRoundCorners index, RectParts corners) { + auto parts = RectPart::Top + | RectPart::NoTopBottom + | RectPart::Bottom + | corners; + FillRoundRect(p, rect, bg, index, nullptr, parts); + if ((corners & RectPart::AllCorners) != RectPart::AllCorners) { + const auto size = Corners[index].p[0].width() / style::DevicePixelRatio(); + if (!(corners & RectPart::TopLeft)) { + p.fillRect(rect.x(), rect.y(), size, size, bg); + } + if (!(corners & RectPart::TopRight)) { + p.fillRect(rect.x() + rect.width() - size, rect.y(), size, size, bg); + } + if (!(corners & RectPart::BottomLeft)) { + p.fillRect(rect.x(), rect.y() + rect.height() - size, size, size, bg); + } + if (!(corners & RectPart::BottomRight)) { + p.fillRect(rect.x() + rect.width() - size, rect.y() + rect.height() - size, size, size, bg); + } + } +} + +void FillComplexOverlayRect(Painter &p, QRect rect, ImageRoundRadius radius, RectParts corners) { + if (radius == ImageRoundRadius::Ellipse) { + PainterHighQualityEnabler hq(p); + p.setPen(Qt::NoPen); + p.setBrush(p.textPalette().selectOverlay); + p.drawEllipse(rect); + } else { + auto overlayCorners = (radius == ImageRoundRadius::Small) + ? SelectedOverlaySmallCorners + : SelectedOverlayLargeCorners; + const auto bg = p.textPalette().selectOverlay; + RectWithCorners(p, rect, bg, overlayCorners, corners); + } +} + +void FillComplexLocationRect(Painter &p, QRect rect, ImageRoundRadius radius, RectParts corners) { + RectWithCorners(p, rect, st::msgInBg, MessageInCorners, corners); +} + +void FillRoundRect(Painter &p, int32 x, int32 y, int32 w, int32 h, style::color bg, const CornersPixmaps &corner, const style::color *shadow, RectParts parts) { + auto cornerWidth = corner.p[0].width() / style::DevicePixelRatio(); + auto cornerHeight = corner.p[0].height() / style::DevicePixelRatio(); + if (w < 2 * cornerWidth || h < 2 * cornerHeight) return; + if (w > 2 * cornerWidth) { + if (parts & RectPart::Top) { + p.fillRect(x + cornerWidth, y, w - 2 * cornerWidth, cornerHeight, bg); + } + if (parts & RectPart::Bottom) { + p.fillRect(x + cornerWidth, y + h - cornerHeight, w - 2 * cornerWidth, cornerHeight, bg); + if (shadow) { + p.fillRect(x + cornerWidth, y + h, w - 2 * cornerWidth, st::msgShadow, *shadow); + } + } + } + if (h > 2 * cornerHeight) { + if ((parts & RectPart::NoTopBottom) == RectPart::NoTopBottom) { + p.fillRect(x, y + cornerHeight, w, h - 2 * cornerHeight, bg); + } else { + if (parts & RectPart::Left) { + p.fillRect(x, y + cornerHeight, cornerWidth, h - 2 * cornerHeight, bg); + } + if ((parts & RectPart::Center) && w > 2 * cornerWidth) { + p.fillRect(x + cornerWidth, y + cornerHeight, w - 2 * cornerWidth, h - 2 * cornerHeight, bg); + } + if (parts & RectPart::Right) { + p.fillRect(x + w - cornerWidth, y + cornerHeight, cornerWidth, h - 2 * cornerHeight, bg); + } + } + } + if (parts & RectPart::TopLeft) { + p.drawPixmap(x, y, corner.p[0]); + } + if (parts & RectPart::TopRight) { + p.drawPixmap(x + w - cornerWidth, y, corner.p[1]); + } + if (parts & RectPart::BottomLeft) { + p.drawPixmap(x, y + h - cornerHeight, corner.p[2]); + } + if (parts & RectPart::BottomRight) { + p.drawPixmap(x + w - cornerWidth, y + h - cornerHeight, corner.p[3]); + } +} + +void FillRoundRect(Painter &p, int32 x, int32 y, int32 w, int32 h, style::color bg, CachedRoundCorners index, const style::color *shadow, RectParts parts) { + FillRoundRect(p, x, y, w, h, bg, Corners[index], shadow, parts); +} + +void FillRoundShadow(Painter &p, int32 x, int32 y, int32 w, int32 h, style::color shadow, CachedRoundCorners index, RectParts parts) { + auto &corner = Corners[index]; + auto cornerWidth = corner.p[0].width() / style::DevicePixelRatio(); + auto cornerHeight = corner.p[0].height() / style::DevicePixelRatio(); + if (parts & RectPart::Bottom) { + p.fillRect(x + cornerWidth, y + h, w - 2 * cornerWidth, st::msgShadow, shadow); + } + if (parts & RectPart::BottomLeft) { + p.fillRect(x, y + h - cornerHeight, cornerWidth, st::msgShadow, shadow); + p.drawPixmap(x, y + h - cornerHeight + st::msgShadow, corner.p[2]); + } + if (parts & RectPart::BottomRight) { + p.fillRect(x + w - cornerWidth, y + h - cornerHeight, cornerWidth, st::msgShadow, shadow); + p.drawPixmap(x + w - cornerWidth, y + h - cornerHeight + st::msgShadow, corner.p[3]); + } +} + +void FillRoundRect(Painter &p, int32 x, int32 y, int32 w, int32 h, style::color bg, ImageRoundRadius radius, RectParts parts) { + auto colorKey = ((uint32(bg->c.alpha()) & 0xFF) << 24) | ((uint32(bg->c.red()) & 0xFF) << 16) | ((uint32(bg->c.green()) & 0xFF) << 8) | ((uint32(bg->c.blue()) & 0xFF) << 24); + auto i = CornersMap.find(colorKey); + if (i == CornersMap.cend()) { + QImage images[4]; + switch (radius) { + case ImageRoundRadius::Small: PrepareCorners(SmallMaskCorners, st::buttonRadius, bg, nullptr, images); break; + case ImageRoundRadius::Large: PrepareCorners(LargeMaskCorners, st::historyMessageRadius, bg, nullptr, images); break; + default: p.fillRect(x, y, w, h, bg); return; + } + + CornersPixmaps pixmaps; + for (int j = 0; j < 4; ++j) { + pixmaps.p[j] = PixmapFromImage(std::move(images[j])); + pixmaps.p[j].setDevicePixelRatio(style::DevicePixelRatio()); + } + i = CornersMap.emplace(colorKey, pixmaps).first; + } + FillRoundRect(p, x, y, w, h, bg, i->second, nullptr, parts); +} + +} // namespace Ui diff --git a/Telegram/SourceFiles/ui/cached_round_corners.h b/Telegram/SourceFiles/ui/cached_round_corners.h new file mode 100644 index 000000000..92e61df08 --- /dev/null +++ b/Telegram/SourceFiles/ui/cached_round_corners.h @@ -0,0 +1,75 @@ +/* +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 +*/ +#pragma once + +#include "ui/rect_part.h" + +class Painter; + +enum class ImageRoundRadius; + +namespace Ui { + +enum CachedRoundCorners : int { + SmallMaskCorners = 0x00, // for images + LargeMaskCorners, + + BoxCorners, + MenuCorners, + BotKbOverCorners, + StickerCorners, + StickerSelectedCorners, + SelectedOverlaySmallCorners, + SelectedOverlayLargeCorners, + DateCorners, + DateSelectedCorners, + OverviewVideoCorners, + OverviewVideoSelectedCorners, + ForwardCorners, + MediaviewSaveCorners, + EmojiHoverCorners, + StickerHoverCorners, + BotKeyboardCorners, + PhotoSelectOverlayCorners, + + Doc1Corners, + Doc2Corners, + Doc3Corners, + Doc4Corners, + + InShadowCorners, // for photos without bg + InSelectedShadowCorners, + + MessageInCorners, // with shadow + MessageInSelectedCorners, + MessageOutCorners, + MessageOutSelectedCorners, + + RoundCornersCount +}; + +void FillComplexOverlayRect(Painter &p, QRect rect, ImageRoundRadius radius, RectParts corners); +void FillComplexLocationRect(Painter &p, QRect rect, ImageRoundRadius radius, RectParts corners); + +void FillRoundRect(Painter &p, int32 x, int32 y, int32 w, int32 h, style::color bg, CachedRoundCorners index, const style::color *shadow = nullptr, RectParts parts = RectPart::Full); +inline void FillRoundRect(Painter &p, const QRect &rect, style::color bg, CachedRoundCorners index, const style::color *shadow = nullptr, RectParts parts = RectPart::Full) { + return FillRoundRect(p, rect.x(), rect.y(), rect.width(), rect.height(), bg, index, shadow, parts); +} +void FillRoundShadow(Painter &p, int32 x, int32 y, int32 w, int32 h, style::color shadow, CachedRoundCorners index, RectParts parts = RectPart::Full); +inline void FillRoundShadow(Painter &p, const QRect &rect, style::color shadow, CachedRoundCorners index, RectParts parts = RectPart::Full) { + return FillRoundShadow(p, rect.x(), rect.y(), rect.width(), rect.height(), shadow, index, parts); +} +void FillRoundRect(Painter &p, int32 x, int32 y, int32 w, int32 h, style::color bg, ImageRoundRadius radius, RectParts parts = RectPart::Full); +inline void FillRoundRect(Painter &p, const QRect &rect, style::color bg, ImageRoundRadius radius, RectParts parts = RectPart::Full) { + return FillRoundRect(p, rect.x(), rect.y(), rect.width(), rect.height(), bg, radius, parts); +} + +void StartCachedCorners(); +void FinishCachedCorners(); + +} // namespace Ui diff --git a/Telegram/SourceFiles/ui/chat/attach/attach_album_preview.cpp b/Telegram/SourceFiles/ui/chat/attach/attach_album_preview.cpp new file mode 100644 index 000000000..f7086ccd6 --- /dev/null +++ b/Telegram/SourceFiles/ui/chat/attach/attach_album_preview.cpp @@ -0,0 +1,484 @@ +/* +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 "ui/chat/attach/attach_album_preview.h" + +#include "ui/chat/attach/attach_album_thumbnail.h" +#include "ui/chat/attach/attach_prepare.h" +#include "styles/style_chat.h" +#include "styles/style_boxes.h" +#include "styles/style_layers.h" + +namespace Ui { +namespace { + +constexpr auto kDragDuration = crl::time(200); + +} // namespace + +AlbumPreview::AlbumPreview( + QWidget *parent, + const PreparedList &list, + SendFilesWay way) +: RpWidget(parent) +, _list(list) +, _sendWay(way) { + setMouseTracking(true); + prepareThumbs(); + updateSize(); + updateFileRows(); +} + +AlbumPreview::~AlbumPreview() = default; + +void AlbumPreview::setSendWay(SendFilesWay way) { + if (_sendWay != way) { + cancelDrag(); + _sendWay = way; + } + updateSize(); + updateFileRows(); + update(); +} + +void AlbumPreview::updateFileRows() { + Expects(_order.size() == _thumbs.size()); + const auto isFile = (_sendWay == SendFilesWay::Files); + for (auto i = 0; i < _order.size(); i++) { + _thumbs[i]->updateFileRow(isFile ? _order[i] : -1); + } +} + +std::vector AlbumPreview::takeOrder() { + auto reordered = std::vector>(); + reordered.reserve(_thumbs.size()); + for (auto index : _order) { + reordered.push_back(std::move(_thumbs[index])); + } + _thumbs = std::move(reordered); + return std::exchange(_order, defaultOrder()); +} + +auto AlbumPreview::generateOrderedLayout() const +-> std::vector { + auto sizes = ranges::view::all( + _order + ) | ranges::view::transform([&](int index) { + return _list.files[index].shownDimensions; + }) | ranges::to_vector; + + auto layout = LayoutMediaGroup( + sizes, + st::sendMediaPreviewSize, + st::historyGroupWidthMin / 2, + st::historyGroupSkip / 2); + Assert(layout.size() == _order.size()); + return layout; +} + +std::vector AlbumPreview::defaultOrder() const { + const auto count = int(_list.files.size()); + return ranges::view::ints(0, count) | ranges::to_vector; +} + +void AlbumPreview::prepareThumbs() { + _order = defaultOrder(); + + const auto count = int(_list.files.size()); + const auto layout = generateOrderedLayout(); + _thumbs.reserve(count); + for (auto i = 0; i != count; ++i) { + _thumbs.push_back(std::make_unique( + _list.files[i], + layout[i], + this, + [=] { changeThumbByIndex(thumbIndex(thumbUnderCursor())); }, + [=] { deleteThumbByIndex(thumbIndex(thumbUnderCursor())); })); + } + _thumbsHeight = countLayoutHeight(layout); + _photosHeight = ranges::accumulate(ranges::view::all( + _thumbs + ) | ranges::view::transform([](const auto &thumb) { + return thumb->photoHeight(); + }), 0) + (count - 1) * st::sendMediaPreviewPhotoSkip; + + _filesHeight = count * st::sendMediaFileThumbSize + + (count - 1) * st::sendMediaFileThumbSkip; +} + +int AlbumPreview::contentLeft() const { + return (st::boxWideWidth - st::sendMediaPreviewSize) / 2; +} + +int AlbumPreview::contentTop() const { + return 0; +} + +AlbumThumbnail *AlbumPreview::findThumb(QPoint position) const { + position -= QPoint(contentLeft(), contentTop()); + + auto top = 0; + const auto isPhotosWay = (_sendWay == SendFilesWay::Photos); + const auto skip = isPhotosWay + ? st::sendMediaPreviewPhotoSkip + : st::sendMediaFileThumbSkip; + auto find = [&](const auto &thumb) { + if (_sendWay == SendFilesWay::Album) { + return thumb->containsPoint(position); + } else if (isPhotosWay || _sendWay == SendFilesWay::Files) { + const auto bottom = top + (isPhotosWay + ? thumb->photoHeight() + : st::sendMediaFileThumbSize); + const auto isUnderTop = (position.y() > top); + top = bottom + skip; + return isUnderTop && (position.y() < bottom); + } + return false; + }; + + const auto i = ranges::find_if(_thumbs, std::move(find)); + return (i == _thumbs.end()) ? nullptr : i->get(); +} + +not_null AlbumPreview::findClosestThumb( + QPoint position) const { + Expects(_draggedThumb != nullptr); + + if (const auto exact = findThumb(position)) { + return exact; + } + auto result = _draggedThumb; + auto distance = _draggedThumb->distanceTo(position); + for (const auto &thumb : _thumbs) { + const auto check = thumb->distanceTo(position); + if (check < distance) { + distance = check; + result = thumb.get(); + } + } + return result; +} + +int AlbumPreview::orderIndex( + not_null thumb) const { + const auto i = ranges::find_if(_order, [&](int index) { + return (_thumbs[index].get() == thumb); + }); + Assert(i != _order.end()); + return int(i - _order.begin()); +} + +void AlbumPreview::cancelDrag() { + _thumbsHeightAnimation.stop(); + _finishDragAnimation.stop(); + _shrinkAnimation.stop(); + if (_draggedThumb) { + _draggedThumb->moveInAlbum({ 0, 0 }); + _draggedThumb = nullptr; + } + if (_suggestedThumb) { + const auto suggestedIndex = orderIndex(_suggestedThumb); + if (suggestedIndex > 0) { + _thumbs[_order[suggestedIndex - 1]]->suggestMove(0., [] {}); + } + if (suggestedIndex < int(_order.size() - 1)) { + _thumbs[_order[suggestedIndex + 1]]->suggestMove(0., [] {}); + } + _suggestedThumb->suggestMove(0., [] {}); + _suggestedThumb->finishAnimations(); + _suggestedThumb = nullptr; + } + _paintedAbove = nullptr; + update(); +} + +void AlbumPreview::finishDrag() { + Expects(_draggedThumb != nullptr); + Expects(_suggestedThumb != nullptr); + + if (_suggestedThumb != _draggedThumb) { + const auto currentIndex = orderIndex(_draggedThumb); + const auto newIndex = orderIndex(_suggestedThumb); + const auto delta = (currentIndex < newIndex) ? 1 : -1; + const auto realIndex = _order[currentIndex]; + for (auto i = currentIndex; i != newIndex; i += delta) { + _order[i] = _order[i + delta]; + } + _order[newIndex] = realIndex; + const auto layout = generateOrderedLayout(); + for (auto i = 0, count = int(_order.size()); i != count; ++i) { + _thumbs[_order[i]]->moveToLayout(layout[i]); + } + _finishDragAnimation.start([=] { update(); }, 0., 1., kDragDuration); + + updateSizeAnimated(layout); + } else { + for (const auto &thumb : _thumbs) { + thumb->resetLayoutAnimation(); + } + _draggedThumb->animateLayoutToInitial(); + _finishDragAnimation.start([=] { update(); }, 0., 1., kDragDuration); + } +} + +int AlbumPreview::countLayoutHeight( + const std::vector &layout) const { + const auto accumulator = [](int current, const auto &item) { + return std::max(current, item.geometry.y() + item.geometry.height()); + }; + return ranges::accumulate(layout, 0, accumulator); +} + +void AlbumPreview::updateSizeAnimated( + const std::vector &layout) { + const auto newHeight = countLayoutHeight(layout); + if (newHeight != _thumbsHeight) { + _thumbsHeightAnimation.start( + [=] { updateSize(); }, + _thumbsHeight, + newHeight, + kDragDuration); + _thumbsHeight = newHeight; + } +} + +void AlbumPreview::updateSize() { + const auto newHeight = [&] { + switch (_sendWay) { + case SendFilesWay::Album: + return int(std::round(_thumbsHeightAnimation.value( + _thumbsHeight))); + case SendFilesWay::Photos: return _photosHeight; + case SendFilesWay::Files: return _filesHeight; + } + Unexpected("Send way in AlbumPreview::updateSize"); + }(); + if (height() != newHeight) { + resize(st::boxWideWidth, newHeight); + } +} + +void AlbumPreview::paintEvent(QPaintEvent *e) { + Painter p(this); + + switch (_sendWay) { + case SendFilesWay::Album: paintAlbum(p); break; + case SendFilesWay::Photos: paintPhotos(p, e->rect()); break; + case SendFilesWay::Files: paintFiles(p, e->rect()); break; + } +} + +void AlbumPreview::paintAlbum(Painter &p) const { + const auto shrink = _shrinkAnimation.value(_draggedThumb ? 1. : 0.); + const auto moveProgress = _finishDragAnimation.value(1.); + const auto left = contentLeft(); + const auto top = contentTop(); + for (const auto &thumb : _thumbs) { + if (thumb.get() != _paintedAbove) { + thumb->paintInAlbum(p, left, top, shrink, moveProgress); + } + } + if (_paintedAbove) { + _paintedAbove->paintInAlbum(p, left, top, shrink, moveProgress); + } +} + +void AlbumPreview::paintPhotos(Painter &p, QRect clip) const { + const auto left = (st::boxWideWidth - st::sendMediaPreviewSize) / 2; + auto top = 0; + const auto outerWidth = width(); + for (const auto &thumb : _thumbs) { + const auto bottom = top + thumb->photoHeight(); + const auto guard = gsl::finally([&] { + top = bottom + st::sendMediaPreviewPhotoSkip; + }); + if (top >= clip.y() + clip.height()) { + break; + } else if (bottom <= clip.y()) { + continue; + } + thumb->paintPhoto(p, left, top, outerWidth); + } +} + +void AlbumPreview::paintFiles(Painter &p, QRect clip) const { + const auto fileHeight = st::sendMediaFileThumbSize + + st::sendMediaFileThumbSkip; + const auto bottom = clip.y() + clip.height(); + const auto from = std::clamp(clip.y() / fileHeight, 0, int(_thumbs.size())); + const auto till = std::clamp((bottom + fileHeight - 1) / fileHeight, 0, int(_thumbs.size())); + const auto left = (st::boxWideWidth - st::sendMediaPreviewSize) / 2; + const auto outerWidth = width(); + + auto top = from * fileHeight; + for (auto i = from; i != till; ++i) { + _thumbs[i]->paintFile(p, left, top, outerWidth); + top += fileHeight; + } +} + +int AlbumPreview::thumbIndex(AlbumThumbnail *thumb) { + if (!thumb) { + return -1; + } + const auto thumbIt = ranges::find_if(_thumbs, [&](auto &t) { + return t.get() == thumb; + }); + Expects(thumbIt != _thumbs.end()); + return std::distance(_thumbs.begin(), thumbIt); +} + +AlbumThumbnail *AlbumPreview::thumbUnderCursor() { + return findThumb(mapFromGlobal(QCursor::pos())); +} + +void AlbumPreview::deleteThumbByIndex(int index) { + if (index < 0) { + return; + } + const auto orderIt = ranges::find(_order, index); + Expects(orderIt != _order.end()); + + _order.erase(orderIt); + ranges::for_each(_order, [=](auto &i) { + if (i > index) { + i--; + } + }); + _thumbDeleted.fire(std::move(index)); +} + +void AlbumPreview::changeThumbByIndex(int index) { + if (index < 0) { + return; + } + _thumbChanged.fire(std::move(index)); +} + +void AlbumPreview::thumbButtonsCallback( + not_null thumb, + AttachButtonType type) { + const auto index = thumbIndex(thumb); + + switch (type) { + case AttachButtonType::None: return; + case AttachButtonType::Edit: changeThumbByIndex(index); break; + case AttachButtonType::Delete: deleteThumbByIndex(index); break; + } +} + +void AlbumPreview::mousePressEvent(QMouseEvent *e) { + if (_finishDragAnimation.animating()) { + return; + } + const auto position = e->pos(); + cancelDrag(); + if (const auto thumb = findThumb(position)) { + if (thumb->buttonsContainPoint(e->pos())) { + thumbButtonsCallback(thumb, thumb->buttonTypeFromPoint(e->pos())); + return; + } + _paintedAbove = _suggestedThumb = _draggedThumb = thumb; + _draggedStartPosition = position; + _shrinkAnimation.start( + [=] { update(); }, + 0., + 1., + AlbumThumbnail::kShrinkDuration); + } +} + +void AlbumPreview::mouseMoveEvent(QMouseEvent *e) { + if (_sendWay == SendFilesWay::Files) { + applyCursor(style::cur_default); + return; + } + const auto isAlbum = (_sendWay == SendFilesWay::Album); + if (isAlbum && _draggedThumb) { + const auto position = e->pos(); + _draggedThumb->moveInAlbum(position - _draggedStartPosition); + updateSuggestedDrag(_draggedThumb->center()); + update(); + } else { + const auto thumb = findThumb(e->pos()); + const auto regularCursor = isAlbum + ? style::cur_sizeall + : style::cur_default; + const auto cursor = thumb + ? (thumb->buttonsContainPoint(e->pos()) + ? style::cur_pointer + : regularCursor) + : style::cur_default; + applyCursor(cursor); + } +} + +void AlbumPreview::applyCursor(style::cursor cursor) { + if (_cursor != cursor) { + _cursor = cursor; + setCursor(_cursor); + } +} + +void AlbumPreview::updateSuggestedDrag(QPoint position) { + auto closest = findClosestThumb(position); + auto closestIndex = orderIndex(closest); + + const auto draggedIndex = orderIndex(_draggedThumb); + const auto closestIsBeforePoint = closest->isPointAfter(position); + if (closestIndex < draggedIndex && closestIsBeforePoint) { + closest = _thumbs[_order[++closestIndex]].get(); + } else if (closestIndex > draggedIndex && !closestIsBeforePoint) { + closest = _thumbs[_order[--closestIndex]].get(); + } + + if (_suggestedThumb == closest) { + return; + } + + const auto last = int(_order.size()) - 1; + if (_suggestedThumb) { + const auto suggestedIndex = orderIndex(_suggestedThumb); + if (suggestedIndex < draggedIndex && suggestedIndex > 0) { + const auto previous = _thumbs[_order[suggestedIndex - 1]].get(); + previous->suggestMove(0., [=] { update(); }); + } else if (suggestedIndex > draggedIndex && suggestedIndex < last) { + const auto next = _thumbs[_order[suggestedIndex + 1]].get(); + next->suggestMove(0., [=] { update(); }); + } + _suggestedThumb->suggestMove(0., [=] { update(); }); + } + _suggestedThumb = closest; + const auto suggestedIndex = closestIndex; + if (_suggestedThumb != _draggedThumb) { + const auto delta = (suggestedIndex < draggedIndex) ? 1. : -1.; + if (delta > 0. && suggestedIndex > 0) { + const auto previous = _thumbs[_order[suggestedIndex - 1]].get(); + previous->suggestMove(-delta, [=] { update(); }); + } else if (delta < 0. && suggestedIndex < last) { + const auto next = _thumbs[_order[suggestedIndex + 1]].get(); + next->suggestMove(-delta, [=] { update(); }); + } + _suggestedThumb->suggestMove(delta, [=] { update(); }); + } +} + +void AlbumPreview::mouseReleaseEvent(QMouseEvent *e) { + if (_draggedThumb) { + finishDrag(); + _shrinkAnimation.start( + [=] { update(); }, + 1., + 0., + AlbumThumbnail::kShrinkDuration); + _draggedThumb = nullptr; + _suggestedThumb = nullptr; + update(); + } +} + +} // namespace Ui diff --git a/Telegram/SourceFiles/ui/chat/attach/attach_album_preview.h b/Telegram/SourceFiles/ui/chat/attach/attach_album_preview.h new file mode 100644 index 000000000..752cc2af7 --- /dev/null +++ b/Telegram/SourceFiles/ui/chat/attach/attach_album_preview.h @@ -0,0 +1,99 @@ +/* +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 +*/ +#pragma once + +#include "ui/rp_widget.h" +#include "ui/chat/attach/attach_common.h" + +namespace Ui { + +struct PreparedList; +struct GroupMediaLayout; +class AlbumThumbnail; + +class AlbumPreview final : public RpWidget { +public: + AlbumPreview( + QWidget *parent, + const PreparedList &list, + SendFilesWay way); + ~AlbumPreview(); + + void setSendWay(SendFilesWay way); + std::vector takeOrder(); + + auto thumbDeleted() { + return _thumbDeleted.events(); + } + + auto thumbChanged() { + return _thumbChanged.events(); + } + +protected: + void paintEvent(QPaintEvent *e) override; + void mousePressEvent(QMouseEvent *e) override; + void mouseMoveEvent(QMouseEvent *e) override; + void mouseReleaseEvent(QMouseEvent *e) override; + +private: + int countLayoutHeight( + const std::vector &layout) const; + std::vector generateOrderedLayout() const; + std::vector defaultOrder() const; + void prepareThumbs(); + void updateSizeAnimated(const std::vector &layout); + void updateSize(); + void updateFileRows(); + + int thumbIndex(AlbumThumbnail *thumb); + AlbumThumbnail *thumbUnderCursor(); + void deleteThumbByIndex(int index); + void changeThumbByIndex(int index); + void thumbButtonsCallback( + not_null thumb, + AttachButtonType type); + + void paintAlbum(Painter &p) const; + void paintPhotos(Painter &p, QRect clip) const; + void paintFiles(Painter &p, QRect clip) const; + + void applyCursor(style::cursor cursor); + int contentLeft() const; + int contentTop() const; + AlbumThumbnail *findThumb(QPoint position) const; + not_null findClosestThumb(QPoint position) const; + void updateSuggestedDrag(QPoint position); + int orderIndex(not_null thumb) const; + void cancelDrag(); + void finishDrag(); + + const PreparedList &_list; + SendFilesWay _sendWay = SendFilesWay::Files; + style::cursor _cursor = style::cur_default; + std::vector _order; + std::vector> _thumbs; + int _thumbsHeight = 0; + int _photosHeight = 0; + int _filesHeight = 0; + + AlbumThumbnail *_draggedThumb = nullptr; + AlbumThumbnail *_suggestedThumb = nullptr; + AlbumThumbnail *_paintedAbove = nullptr; + QPoint _draggedStartPosition; + + rpl::event_stream _thumbDeleted; + rpl::event_stream _thumbChanged; + + mutable Animations::Simple _thumbsHeightAnimation; + mutable Animations::Simple _shrinkAnimation; + mutable Animations::Simple _finishDragAnimation; + +}; + +} // namespace Ui diff --git a/Telegram/SourceFiles/ui/chat/attach/attach_album_thumbnail.cpp b/Telegram/SourceFiles/ui/chat/attach/attach_album_thumbnail.cpp new file mode 100644 index 000000000..7664a2168 --- /dev/null +++ b/Telegram/SourceFiles/ui/chat/attach/attach_album_thumbnail.cpp @@ -0,0 +1,543 @@ +/* +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 "ui/chat/attach/attach_album_thumbnail.h" + +#include "ui/chat/attach/attach_prepare.h" +#include "ui/image/image_prepare.h" +#include "ui/text/format_values.h" +#include "ui/widgets/buttons.h" +#include "ui/ui_utility.h" +#include "base/call_delayed.h" +#include "styles/style_chat.h" +#include "styles/style_boxes.h" + +#include + +namespace Ui { + +AlbumThumbnail::AlbumThumbnail( + const PreparedFile &file, + const GroupMediaLayout &layout, + QWidget *parent, + Fn editCallback, + Fn deleteCallback) +: _layout(layout) +, _fullPreview(file.preview) +, _shrinkSize(int(std::ceil(st::historyMessageRadius / 1.4))) +, _isVideo(file.type == PreparedFile::AlbumType::Video) +, _buttonsRect(st::sendBoxAlbumGroupRadius, st::callFingerprintBg) { + Expects(!_fullPreview.isNull()); + + moveToLayout(layout); + + using Option = Images::Option; + const auto previewWidth = _fullPreview.width(); + const auto previewHeight = _fullPreview.height(); + const auto imageWidth = std::max( + previewWidth / style::DevicePixelRatio(), + st::minPhotoSize); + const auto imageHeight = std::max( + previewHeight / style::DevicePixelRatio(), + st::minPhotoSize); + _photo = PixmapFromImage(Images::prepare( + _fullPreview, + previewWidth, + previewHeight, + Option::RoundedLarge | Option::RoundedAll, + imageWidth, + imageHeight)); + + const auto idealSize = st::sendMediaFileThumbSize * style::DevicePixelRatio(); + const auto fileThumbSize = (previewWidth > previewHeight) + ? QSize(previewWidth * idealSize / previewHeight, idealSize) + : QSize(idealSize, previewHeight * idealSize / previewWidth); + _fileThumb = PixmapFromImage(Images::prepare( + _fullPreview, + fileThumbSize.width(), + fileThumbSize.height(), + Option::RoundedSmall | Option::RoundedAll, + st::sendMediaFileThumbSize, + st::sendMediaFileThumbSize + )); + + const auto availableFileWidth = st::sendMediaPreviewSize + - st::sendMediaFileThumbSkip + - st::sendMediaFileThumbSize + // Right buttons. + - st::sendBoxAlbumGroupButtonFile.width * 2 + - st::sendBoxAlbumGroupEditInternalSkip * 2 + - st::sendBoxAlbumGroupSkipRight; + const auto filepath = file.path; + if (filepath.isEmpty()) { + //_name = filedialogDefaultName( // #TODO files + // u"image"_q, + // u".png"_q, + // QString(), + // true); + _name = "image.png"; + _status = u"%1x%2"_q.arg( + _fullPreview.width() + ).arg( + _fullPreview.height() + ); + } else { + auto fileinfo = QFileInfo(filepath); + _name = fileinfo.fileName(); + _status = FormatSizeText(fileinfo.size()); + } + _nameWidth = st::semiboldFont->width(_name); + if (_nameWidth > availableFileWidth) { + _name = st::semiboldFont->elided( + _name, + availableFileWidth, + Qt::ElideMiddle); + _nameWidth = st::semiboldFont->width(_name); + } + _statusWidth = st::normalFont->width(_status); + + _editMedia.create(parent, st::sendBoxAlbumGroupButtonFile); + _deleteMedia.create(parent, st::sendBoxAlbumGroupButtonFile); + + const auto duration = st::historyAttach.ripple.hideDuration; + _editMedia->setClickedCallback([=] { + base::call_delayed(duration, parent, editCallback); + }); + _deleteMedia->setClickedCallback([=] { + base::call_delayed(duration, parent, deleteCallback); + }); + + _editMedia->setIconOverride(&st::editMediaButtonIconFile); + _deleteMedia->setIconOverride(&st::sendBoxAlbumGroupDeleteButtonIconFile); + + updateFileRow(-1); +} + +void AlbumThumbnail::updateFileRow(int row) { + if (row < 0) { + _editMedia->hide(); + _deleteMedia->hide(); + return; + } + _editMedia->show(); + _deleteMedia->show(); + + const auto fileHeight = st::sendMediaFileThumbSize + + st::sendMediaFileThumbSkip; + + const auto top = row * fileHeight + st::sendBoxAlbumGroupSkipTop; + const auto size = st::editMediaButtonSize; + + auto right = st::sendBoxAlbumGroupSkipRight + size; + _deleteMedia->moveToRight(right, top); + right += st::sendBoxAlbumGroupEditInternalSkip + size; + _editMedia->moveToRight(right, top); +} + +void AlbumThumbnail::resetLayoutAnimation() { + _animateFromGeometry = std::nullopt; +} + +void AlbumThumbnail::animateLayoutToInitial() { + _animateFromGeometry = countRealGeometry(); + _suggestedMove = 0.; + _albumPosition = QPoint(0, 0); +} + +void AlbumThumbnail::moveToLayout(const GroupMediaLayout &layout) { + animateLayoutToInitial(); + _layout = layout; + + const auto width = _layout.geometry.width(); + const auto height = _layout.geometry.height(); + _albumCorners = GetCornersFromSides(_layout.sides); + using Option = Images::Option; + const auto options = Option::Smooth + | Option::RoundedLarge + | ((_albumCorners & RectPart::TopLeft) + ? Option::RoundedTopLeft + : Option::None) + | ((_albumCorners & RectPart::TopRight) + ? Option::RoundedTopRight + : Option::None) + | ((_albumCorners & RectPart::BottomLeft) + ? Option::RoundedBottomLeft + : Option::None) + | ((_albumCorners & RectPart::BottomRight) + ? Option::RoundedBottomRight + : Option::None); + const auto pixSize = GetImageScaleSizeForGeometry( + { _fullPreview.width(), _fullPreview.height() }, + { width, height }); + const auto pixWidth = pixSize.width() * style::DevicePixelRatio(); + const auto pixHeight = pixSize.height() * style::DevicePixelRatio(); + + _albumImage = PixmapFromImage(Images::prepare( + _fullPreview, + pixWidth, + pixHeight, + options, + width, + height)); +} + +int AlbumThumbnail::photoHeight() const { + return _photo.height() / style::DevicePixelRatio(); +} + +void AlbumThumbnail::paintInAlbum( + Painter &p, + int left, + int top, + float64 shrinkProgress, + float64 moveProgress) { + const auto shrink = anim::interpolate(0, _shrinkSize, shrinkProgress); + _lastShrinkValue = shrink; + const auto geometry = countCurrentGeometry(moveProgress); + const auto x = left + geometry.x(); + const auto y = top + geometry.y(); + if (shrink > 0 || moveProgress < 1.) { + const auto size = geometry.size(); + if (shrinkProgress < 1 && _albumCorners != RectPart::None) { + prepareCache(size, shrink); + p.drawImage(x, y, _albumCache); + } else { + const auto to = QRect({ x, y }, size).marginsRemoved( + { shrink, shrink, shrink, shrink } + ); + drawSimpleFrame(p, to, size); + } + } else { + p.drawPixmap(x, y, _albumImage); + } + if (_isVideo) { + const auto inner = QRect( + x + (geometry.width() - st::msgFileSize) / 2, + y + (geometry.height() - st::msgFileSize) / 2, + st::msgFileSize, + st::msgFileSize); + { + PainterHighQualityEnabler hq(p); + p.setPen(Qt::NoPen); + p.setBrush(st::msgDateImgBg); + p.drawEllipse(inner); + } + st::historyFileThumbPlay.paintInCenter(p, inner); + } + + _lastRectOfButtons = paintButtons( + p, + { x, y }, + geometry.width(), + shrinkProgress); +} + +void AlbumThumbnail::prepareCache(QSize size, int shrink) { + const auto width = std::max( + _layout.geometry.width(), + _animateFromGeometry ? _animateFromGeometry->width() : 0); + const auto height = std::max( + _layout.geometry.height(), + _animateFromGeometry ? _animateFromGeometry->height() : 0); + const auto cacheSize = QSize(width, height) * style::DevicePixelRatio(); + + if (_albumCache.width() < cacheSize.width() + || _albumCache.height() < cacheSize.height()) { + _albumCache = QImage(cacheSize, QImage::Format_ARGB32_Premultiplied); + } + _albumCache.fill(Qt::transparent); + { + Painter p(&_albumCache); + const auto to = QRect(QPoint(), size).marginsRemoved( + { shrink, shrink, shrink, shrink } + ); + drawSimpleFrame(p, to, size); + } + Images::prepareRound( + _albumCache, + ImageRoundRadius::Large, + _albumCorners, + QRect(QPoint(), size * style::DevicePixelRatio())); + _albumCache.setDevicePixelRatio(style::DevicePixelRatio()); +} + +void AlbumThumbnail::drawSimpleFrame(Painter &p, QRect to, QSize size) const { + const auto fullWidth = _fullPreview.width(); + const auto fullHeight = _fullPreview.height(); + const auto previewSize = GetImageScaleSizeForGeometry( + { fullWidth, fullHeight }, + { size.width(), size.height() }); + const auto previewWidth = previewSize.width() * style::DevicePixelRatio(); + const auto previewHeight = previewSize.height() * style::DevicePixelRatio(); + const auto width = size.width() * style::DevicePixelRatio(); + const auto height = size.height() * style::DevicePixelRatio(); + const auto scaleWidth = to.width() / float64(width); + const auto scaleHeight = to.height() / float64(height); + const auto Round = [](float64 value) { + return int(std::round(value)); + }; + const auto [from, fillBlack] = [&] { + if (previewWidth < width && previewHeight < height) { + const auto toWidth = Round(previewWidth * scaleWidth); + const auto toHeight = Round(previewHeight * scaleHeight); + return std::make_pair( + QRect(0, 0, fullWidth, fullHeight), + QMargins( + (to.width() - toWidth) / 2, + (to.height() - toHeight) / 2, + to.width() - toWidth - (to.width() - toWidth) / 2, + to.height() - toHeight - (to.height() - toHeight) / 2)); + } else if (previewWidth * height > previewHeight * width) { + if (previewHeight >= height) { + const auto takeWidth = previewWidth * height / previewHeight; + const auto useWidth = fullWidth * width / takeWidth; + return std::make_pair( + QRect( + (fullWidth - useWidth) / 2, + 0, + useWidth, + fullHeight), + QMargins(0, 0, 0, 0)); + } else { + const auto takeWidth = previewWidth; + const auto useWidth = fullWidth * width / takeWidth; + const auto toHeight = Round(previewHeight * scaleHeight); + const auto toSkip = (to.height() - toHeight) / 2; + return std::make_pair( + QRect( + (fullWidth - useWidth) / 2, + 0, + useWidth, + fullHeight), + QMargins( + 0, + toSkip, + 0, + to.height() - toHeight - toSkip)); + } + } else { + if (previewWidth >= width) { + const auto takeHeight = previewHeight * width / previewWidth; + const auto useHeight = fullHeight * height / takeHeight; + return std::make_pair( + QRect( + 0, + (fullHeight - useHeight) / 2, + fullWidth, + useHeight), + QMargins(0, 0, 0, 0)); + } else { + const auto takeHeight = previewHeight; + const auto useHeight = fullHeight * height / takeHeight; + const auto toWidth = Round(previewWidth * scaleWidth); + const auto toSkip = (to.width() - toWidth) / 2; + return std::make_pair( + QRect( + 0, + (fullHeight - useHeight) / 2, + fullWidth, + useHeight), + QMargins( + toSkip, + 0, + to.width() - toWidth - toSkip, + 0)); + } + } + }(); + + p.drawImage(to.marginsRemoved(fillBlack), _fullPreview, from); + if (fillBlack.top() > 0) { + p.fillRect(to.x(), to.y(), to.width(), fillBlack.top(), st::imageBg); + } + if (fillBlack.bottom() > 0) { + p.fillRect( + to.x(), + to.y() + to.height() - fillBlack.bottom(), + to.width(), + fillBlack.bottom(), + st::imageBg); + } + if (fillBlack.left() > 0) { + p.fillRect( + to.x(), + to.y() + fillBlack.top(), + fillBlack.left(), + to.height() - fillBlack.top() - fillBlack.bottom(), + st::imageBg); + } + if (fillBlack.right() > 0) { + p.fillRect( + to.x() + to.width() - fillBlack.right(), + to.y() + fillBlack.top(), + fillBlack.right(), + to.height() - fillBlack.top() - fillBlack.bottom(), + st::imageBg); + } +} + +void AlbumThumbnail::paintPhoto(Painter &p, int left, int top, int outerWidth) { + const auto width = _photo.width() / style::DevicePixelRatio(); + p.drawPixmapLeft( + left + (st::sendMediaPreviewSize - width) / 2, + top, + outerWidth, + _photo); + + _lastRectOfButtons = paintButtons( + p, + { left, top }, + st::sendMediaPreviewSize, + 0); +} + +void AlbumThumbnail::paintFile(Painter &p, int left, int top, int outerWidth) { + const auto textLeft = left + + st::sendMediaFileThumbSize + + st::sendMediaFileThumbSkip; + + p.drawPixmap(left, top, _fileThumb); + p.setFont(st::semiboldFont); + p.setPen(st::historyFileNameInFg); + p.drawTextLeft( + textLeft, + top + st::sendMediaFileNameTop, + outerWidth, + _name, + _nameWidth); + p.setFont(st::normalFont); + p.setPen(st::mediaInFg); + p.drawTextLeft( + textLeft, + top + st::sendMediaFileStatusTop, + outerWidth, + _status, + _statusWidth); +} + +bool AlbumThumbnail::containsPoint(QPoint position) const { + return _layout.geometry.contains(position); +} + +bool AlbumThumbnail::buttonsContainPoint(QPoint position) const { + return _lastRectOfButtons.contains(position); +} + +AttachButtonType AlbumThumbnail::buttonTypeFromPoint(QPoint position) const { + if (!buttonsContainPoint(position)) { + return AttachButtonType::None; + } + return (position.x() < _lastRectOfButtons.center().x()) + ? AttachButtonType::Edit + : AttachButtonType::Delete; +} + +int AlbumThumbnail::distanceTo(QPoint position) const { + const auto delta = (_layout.geometry.center() - position); + return QPoint::dotProduct(delta, delta); +} + +bool AlbumThumbnail::isPointAfter(QPoint position) const { + return position.x() > _layout.geometry.center().x(); +} + +void AlbumThumbnail::moveInAlbum(QPoint to) { + _albumPosition = to; +} + +QPoint AlbumThumbnail::center() const { + auto realGeometry = _layout.geometry; + realGeometry.moveTopLeft(realGeometry.topLeft() + _albumPosition); + return realGeometry.center(); +} + +void AlbumThumbnail::suggestMove(float64 delta, Fn callback) { + if (_suggestedMove != delta) { + _suggestedMoveAnimation.start( + std::move(callback), + _suggestedMove, + delta, + kShrinkDuration); + _suggestedMove = delta; + } +} + +QRect AlbumThumbnail::countRealGeometry() const { + const auto addLeft = int(std::round( + _suggestedMoveAnimation.value(_suggestedMove) * _lastShrinkValue)); + const auto current = _layout.geometry; + const auto realTopLeft = current.topLeft() + + _albumPosition + + QPoint(addLeft, 0); + return { realTopLeft, current.size() }; +} + +QRect AlbumThumbnail::countCurrentGeometry(float64 progress) const { + const auto now = countRealGeometry(); + if (_animateFromGeometry && progress < 1.) { + return { + anim::interpolate(_animateFromGeometry->x(), now.x(), progress), + anim::interpolate(_animateFromGeometry->y(), now.y(), progress), + anim::interpolate(_animateFromGeometry->width(), now.width(), progress), + anim::interpolate(_animateFromGeometry->height(), now.height(), progress) + }; + } + return now; +} + +void AlbumThumbnail::finishAnimations() { + _suggestedMoveAnimation.stop(); +} + +QRect AlbumThumbnail::paintButtons( + Painter &p, + QPoint point, + int outerWidth, + float64 shrinkProgress) { + const auto skipInternal = st::sendBoxAlbumGroupEditInternalSkip; + const auto size = st::sendBoxAlbumGroupHeight; + const auto skipRight = st::sendBoxAlbumGroupSkipRight; + const auto skipTop = st::sendBoxAlbumGroupSkipTop; + const auto groupWidth = size * 2 + skipInternal; + + // If the width is tiny, it would be better to not display the buttons. + if (groupWidth > outerWidth) { + return QRect(); + } + + // If the width is too small, + // it would be better to display the buttons in the center. + const auto groupX = point.x() + ((groupWidth + skipRight * 2 > outerWidth) + ? (outerWidth - groupWidth) / 2 + : outerWidth - skipRight - groupWidth); + const auto groupY = point.y() + skipTop; + const auto deleteLeft = skipInternal + size; + + p.setOpacity(1.0 - shrinkProgress); + + QRect groupRect(groupX, groupY, groupWidth, size); + _buttonsRect.paint(p, groupRect); + + const auto editP = st::sendBoxAlbumGroupEditButtonIconPosition; + const auto deleteP = st::sendBoxAlbumGroupDeleteButtonIconPosition; + + st::sendBoxAlbumGroupEditButtonIcon.paintInCenter( + p, + QRect(groupX + editP.x(), groupY + editP.y(), size, size)); + st::sendBoxAlbumGroupDeleteButtonIcon.paintInCenter( + p, + QRect( + groupX + deleteLeft + deleteP.x(), + groupY + deleteP.y(), + size, + size)); + p.setOpacity(1); + + return groupRect; +} + +} // namespace Ui diff --git a/Telegram/SourceFiles/ui/chat/attach/attach_album_thumbnail.h b/Telegram/SourceFiles/ui/chat/attach/attach_album_thumbnail.h new file mode 100644 index 000000000..13683d356 --- /dev/null +++ b/Telegram/SourceFiles/ui/chat/attach/attach_album_thumbnail.h @@ -0,0 +1,97 @@ +/* +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 +*/ +#pragma once + +#include "ui/chat/attach/attach_common.h" +#include "ui/effects/animations.h" +#include "ui/grouped_layout.h" +#include "ui/round_rect.h" +#include "base/object_ptr.h" + +namespace Ui { + +struct PreparedFile; +class IconButton; + +class AlbumThumbnail final { +public: + AlbumThumbnail( + const PreparedFile &file, + const GroupMediaLayout &layout, + QWidget *parent, + Fn editCallback, + Fn deleteCallback); + + void moveToLayout(const GroupMediaLayout &layout); + void animateLayoutToInitial(); + void resetLayoutAnimation(); + + int photoHeight() const; + + void paintInAlbum( + Painter &p, + int left, + int top, + float64 shrinkProgress, + float64 moveProgress); + void paintPhoto(Painter &p, int left, int top, int outerWidth); + void paintFile(Painter &p, int left, int top, int outerWidth); + + bool containsPoint(QPoint position) const; + bool buttonsContainPoint(QPoint position) const; + AttachButtonType buttonTypeFromPoint(QPoint position) const; + int distanceTo(QPoint position) const; + bool isPointAfter(QPoint position) const; + void moveInAlbum(QPoint to); + QPoint center() const; + void suggestMove(float64 delta, Fn callback); + void finishAnimations(); + + void updateFileRow(int row); + + static constexpr auto kShrinkDuration = crl::time(150); + +private: + QRect countRealGeometry() const; + QRect countCurrentGeometry(float64 progress) const; + void prepareCache(QSize size, int shrink); + void drawSimpleFrame(Painter &p, QRect to, QSize size) const; + QRect paintButtons( + Painter &p, + QPoint point, + int outerWidth, + float64 shrinkProgress); + + GroupMediaLayout _layout; + std::optional _animateFromGeometry; + const QImage _fullPreview; + const int _shrinkSize = 0; + QPixmap _albumImage; + QImage _albumCache; + QPoint _albumPosition; + RectParts _albumCorners = RectPart::None; + QPixmap _photo; + QPixmap _fileThumb; + QString _name; + QString _status; + int _nameWidth = 0; + int _statusWidth = 0; + bool _isVideo = false; + float64 _suggestedMove = 0.; + Animations::Simple _suggestedMoveAnimation; + int _lastShrinkValue = 0; + RoundRect _buttonsRect; + + QRect _lastRectOfButtons; + + object_ptr _editMedia = nullptr; + object_ptr _deleteMedia = nullptr; + +}; + +} // namespace Ui diff --git a/Telegram/SourceFiles/ui/chat/attach/attach_common.h b/Telegram/SourceFiles/ui/chat/attach/attach_common.h new file mode 100644 index 000000000..89226df88 --- /dev/null +++ b/Telegram/SourceFiles/ui/chat/attach/attach_common.h @@ -0,0 +1,24 @@ +/* +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 +*/ +#pragma once + +namespace Ui { + +enum class AttachButtonType { + Edit, + Delete, + None, +}; + +enum class SendFilesWay { + Album, + Photos, + Files, +}; + +} // namespace Ui diff --git a/Telegram/SourceFiles/ui/chat/attach/attach_single_file_preview.cpp b/Telegram/SourceFiles/ui/chat/attach/attach_single_file_preview.cpp new file mode 100644 index 000000000..43f0e0911 --- /dev/null +++ b/Telegram/SourceFiles/ui/chat/attach/attach_single_file_preview.cpp @@ -0,0 +1,173 @@ +/* +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 "ui/chat/attach/attach_single_file_preview.h" + +#include "ui/chat/attach/attach_prepare.h" +#include "ui/text/format_values.h" +#include "ui/text/text_options.h" +#include "ui/image/image_prepare.h" +#include "ui/cached_round_corners.h" +#include "core/mime_type.h" +#include "styles/style_chat.h" +#include "styles/style_boxes.h" + +#include + +namespace Ui { + +SingleFilePreview::SingleFilePreview( + QWidget *parent, + const PreparedFile &file) +: RpWidget(parent) { + preparePreview(file); +} + +void SingleFilePreview::prepareThumb(const QImage &preview) { + if (preview.isNull()) { + return; + } + + auto originalWidth = preview.width(); + auto originalHeight = preview.height(); + auto thumbWidth = st::msgFileThumbSize; + if (originalWidth > originalHeight) { + thumbWidth = (originalWidth * st::msgFileThumbSize) + / originalHeight; + } + auto options = Images::Option::Smooth + | Images::Option::RoundedSmall + | Images::Option::RoundedTopLeft + | Images::Option::RoundedTopRight + | Images::Option::RoundedBottomLeft + | Images::Option::RoundedBottomRight; + _fileThumb = PixmapFromImage(Images::prepare( + preview, + thumbWidth * style::DevicePixelRatio(), + 0, + options, + st::msgFileThumbSize, + st::msgFileThumbSize)); +} + +void SingleFilePreview::preparePreview(const PreparedFile &file) { + auto preview = QImage(); + if (const auto image = std::get_if( + &file.information->media)) { + preview = image->data; + } else if (const auto video = std::get_if( + &file.information->media)) { + preview = video->thumbnail; + } + prepareThumb(preview); + const auto filepath = file.path; + if (filepath.isEmpty()) { + //auto filename = filedialogDefaultName( + // qsl("image"), + // qsl(".png"), + // QString(), + // true); // #TODO files + auto filename = "image.png"; + _nameText.setText( + st::semiboldTextStyle, + filename, + NameTextOptions()); + _statusText = u"%1x%2"_q.arg(preview.width()).arg(preview.height()); + _statusWidth = qMax(_nameText.maxWidth(), st::normalFont->width(_statusText)); + _fileIsImage = true; + } else { + auto fileinfo = QFileInfo(filepath); + auto filename = fileinfo.fileName(); + _fileIsImage = Core::FileIsImage(filename, Core::MimeTypeForFile(fileinfo).name()); + + auto songTitle = QString(); + auto songPerformer = QString(); + if (file.information) { + if (const auto song = std::get_if( + &file.information->media)) { + songTitle = song->title; + songPerformer = song->performer; + _fileIsAudio = true; + } + } + + const auto nameString = ComposeNameString( + filename, + songTitle, + songPerformer); + _nameText.setText( + st::semiboldTextStyle, + nameString, + NameTextOptions()); + _statusText = FormatSizeText(fileinfo.size()); + _statusWidth = qMax( + _nameText.maxWidth(), + st::normalFont->width(_statusText)); + } +} + +void SingleFilePreview::paintEvent(QPaintEvent *e) { + Painter p(this); + + auto w = width() - st::boxPhotoPadding.left() - st::boxPhotoPadding.right(); + auto h = _fileThumb.isNull() ? (st::msgFilePadding.top() + st::msgFileSize + st::msgFilePadding.bottom()) : (st::msgFileThumbPadding.top() + st::msgFileThumbSize + st::msgFileThumbPadding.bottom()); + auto nameleft = 0, nametop = 0, nameright = 0, statustop = 0, linktop = 0; + if (_fileThumb.isNull()) { + nameleft = st::msgFilePadding.left() + st::msgFileSize + st::msgFilePadding.right(); + nametop = st::msgFileNameTop; + nameright = st::msgFilePadding.left(); + statustop = st::msgFileStatusTop; + } else { + nameleft = st::msgFileThumbPadding.left() + st::msgFileThumbSize + st::msgFileThumbPadding.right(); + nametop = st::msgFileThumbNameTop; + nameright = st::msgFileThumbPadding.left(); + statustop = st::msgFileThumbStatusTop; + linktop = st::msgFileThumbLinkTop; + } + auto namewidth = w - nameleft - (_fileThumb.isNull() ? st::msgFilePadding.left() : st::msgFileThumbPadding.left()); + int32 x = (width() - w) / 2, y = st::boxPhotoPadding.top(); + + FillRoundRect(p, x, y, w, h, st::msgOutBg, MessageOutCorners, &st::msgOutShadow); + + if (_fileThumb.isNull()) { + QRect inner(style::rtlrect(x + st::msgFilePadding.left(), y + st::msgFilePadding.top(), st::msgFileSize, st::msgFileSize, width())); + p.setPen(Qt::NoPen); + p.setBrush(st::msgFileOutBg); + + { + PainterHighQualityEnabler hq(p); + p.drawEllipse(inner); + } + + auto &icon = _fileIsAudio + ? st::historyFileOutPlay + : _fileIsImage + ? st::historyFileOutImage + : st::historyFileOutDocument; + icon.paintInCenter(p, inner); + } else { + QRect rthumb(style::rtlrect(x + st::msgFileThumbPadding.left(), y + st::msgFileThumbPadding.top(), st::msgFileThumbSize, st::msgFileThumbSize, width())); + p.drawPixmap(rthumb.topLeft(), _fileThumb); + } + p.setFont(st::semiboldFont); + p.setPen(st::historyFileNameOutFg); + _nameText.drawLeftElided(p, x + nameleft, y + nametop, namewidth, width()); + + auto &status = st::mediaOutFg; + p.setFont(st::normalFont); + p.setPen(status); + p.drawTextLeft(x + nameleft, y + statustop, width(), _statusText); +} + +rpl::producer SingleFilePreview::desiredHeightValue() const { + auto h = _fileThumb.isNull() + ? (st::msgFilePadding.top() + st::msgFileSize + st::msgFilePadding.bottom()) + : (st::msgFileThumbPadding.top() + st::msgFileThumbSize + st::msgFileThumbPadding.bottom()); + return rpl::single(st::boxPhotoPadding.top() + h + st::msgShadow); +} + +} // namespace Ui diff --git a/Telegram/SourceFiles/ui/chat/attach/attach_single_file_preview.h b/Telegram/SourceFiles/ui/chat/attach/attach_single_file_preview.h new file mode 100644 index 000000000..442106148 --- /dev/null +++ b/Telegram/SourceFiles/ui/chat/attach/attach_single_file_preview.h @@ -0,0 +1,40 @@ +/* +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 +*/ +#pragma once + +#include "ui/rp_widget.h" + +namespace Ui { + +struct PreparedFile; + +class SingleFilePreview final : public RpWidget { +public: + SingleFilePreview( + QWidget *parent, + const PreparedFile &file); + + rpl::producer desiredHeightValue() const override; + +protected: + void paintEvent(QPaintEvent *e) override; + +private: + void preparePreview(const PreparedFile &file); + void prepareThumb(const QImage &preview); + + QPixmap _fileThumb; + Text::String _nameText; + bool _fileIsAudio = false; + bool _fileIsImage = false; + QString _statusText; + int _statusWidth = 0; + +}; + +} // namespace Ui diff --git a/Telegram/SourceFiles/ui/filter_icon_panel.cpp b/Telegram/SourceFiles/ui/filter_icon_panel.cpp index c4df60cf6..e9d3d8f47 100644 --- a/Telegram/SourceFiles/ui/filter_icon_panel.cpp +++ b/Telegram/SourceFiles/ui/filter_icon_panel.cpp @@ -12,9 +12,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/effects/panel_animation.h" #include "ui/ui_utility.h" #include "ui/filter_icons.h" +#include "ui/cached_round_corners.h" #include "lang/lang_keys.h" #include "core/application.h" -#include "app.h" #include "styles/style_chat_helpers.h" #include "styles/style_window.h" @@ -102,7 +102,7 @@ void FilterIconPanel::setupInner() { _inner->paintRequest( ) | rpl::start_with_next([=](QRect clip) { auto p = Painter(_inner); - App::roundRect( + Ui::FillRoundRect( p, _inner->rect(), st::emojiPanBg, @@ -122,11 +122,11 @@ void FilterIconPanel::setupInner() { continue; } if (i == selected) { - App::roundRect( + Ui::FillRoundRect( p, rect, st::emojiPanHover, - StickerHoverCorners); + Ui::StickerHoverCorners); } const auto icon = LookupFilterIcon(kIcons[i]).normal; icon->paintInCenter(p, rect, st::emojiIconFg->c); diff --git a/Telegram/SourceFiles/ui/grouped_layout.cpp b/Telegram/SourceFiles/ui/grouped_layout.cpp index 7b98af8b3..cefc127b5 100644 --- a/Telegram/SourceFiles/ui/grouped_layout.cpp +++ b/Telegram/SourceFiles/ui/grouped_layout.cpp @@ -444,8 +444,8 @@ std::vector ComplexLayouter::CropRatios( constexpr auto kMaxRatio = 2.75; constexpr auto kMinRatio = 0.6667; return (averageRatio > 1.1) - ? snap(ratio, 1., kMaxRatio) - : snap(ratio, kMinRatio, 1.); + ? std::clamp(ratio, 1., kMaxRatio) + : std::clamp(ratio, kMinRatio, 1.); }) | ranges::to_vector; } diff --git a/Telegram/SourceFiles/ui/text/format_values.cpp b/Telegram/SourceFiles/ui/text/format_values.cpp index 0bc069c82..60534c3ad 100644 --- a/Telegram/SourceFiles/ui/text/format_values.cpp +++ b/Telegram/SourceFiles/ui/text/format_values.cpp @@ -128,4 +128,20 @@ QString FillAmountAndCurrency(uint64 amount, const QString ¤cy) { //return currencyText + amountText; } +QString ComposeNameString( + const QString &filename, + const QString &songTitle, + const QString &songPerformer) { + if (songTitle.isEmpty() && songPerformer.isEmpty()) { + return filename.isEmpty() ? u"Unknown File"_q : filename; + } + + if (songPerformer.isEmpty()) { + return songTitle; + } + + auto trackTitle = (songTitle.isEmpty() ? u"Unknown Track"_q : songTitle); + return songPerformer + QString::fromUtf8(" \xe2\x80\x93 ") + trackTitle; +} + } // namespace Ui diff --git a/Telegram/SourceFiles/ui/text/format_values.h b/Telegram/SourceFiles/ui/text/format_values.h index 9fb6c00c8..0e0ce8680 100644 --- a/Telegram/SourceFiles/ui/text/format_values.h +++ b/Telegram/SourceFiles/ui/text/format_values.h @@ -25,4 +25,9 @@ inline constexpr auto FileStatusSizeFailed = 0x7FFFFFF2; uint64 amount, const QString ¤cy); +[[nodiscard]] QString ComposeNameString( + const QString &filename, + const QString &songTitle, + const QString &songPerformer); + } // namespace Ui diff --git a/Telegram/SourceFiles/window/themes/window_theme.h b/Telegram/SourceFiles/window/themes/window_theme.h index ec06f9eb9..0d38ecf14 100644 --- a/Telegram/SourceFiles/window/themes/window_theme.h +++ b/Telegram/SourceFiles/window/themes/window_theme.h @@ -118,7 +118,8 @@ struct BackgroundUpdate { [[nodiscard]] bool paletteChanged() const { return (type == Type::TestingTheme) || (type == Type::RevertingTheme) - || (type == Type::ApplyingEdit); + || (type == Type::ApplyingEdit) + || (type == Type::New); } Type type; bool tiled; diff --git a/Telegram/SourceFiles/window/themes/window_theme_warning.cpp b/Telegram/SourceFiles/window/themes/window_theme_warning.cpp index 09929d22a..13d86a951 100644 --- a/Telegram/SourceFiles/window/themes/window_theme_warning.cpp +++ b/Telegram/SourceFiles/window/themes/window_theme_warning.cpp @@ -10,9 +10,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/buttons.h" #include "ui/widgets/shadow.h" #include "ui/ui_utility.h" +#include "ui/cached_round_corners.h" #include "window/themes/window_theme.h" #include "lang/lang_keys.h" -#include "app.h" #include "styles/style_layers.h" #include "styles/style_boxes.h" @@ -62,7 +62,7 @@ void WarningWidget::paintEvent(QPaintEvent *e) { } Ui::Shadow::paint(p, _inner, width(), st::boxRoundShadow); - App::roundRect(p, _inner, st::boxBg, BoxCorners); + Ui::FillRoundRect(p, _inner, st::boxBg, Ui::BoxCorners); p.setFont(st::boxTitleFont); p.setPen(st::boxTitleFg); diff --git a/Telegram/SourceFiles/window/window_history_hider.cpp b/Telegram/SourceFiles/window/window_history_hider.cpp index df519d3f7..442e6a156 100644 --- a/Telegram/SourceFiles/window/window_history_hider.cpp +++ b/Telegram/SourceFiles/window/window_history_hider.cpp @@ -10,9 +10,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "lang/lang_keys.h" #include "ui/widgets/buttons.h" #include "ui/widgets/shadow.h" +#include "ui/cached_round_corners.h" #include "mainwidget.h" #include "facades.h" -#include "app.h" #include "styles/style_layers.h" #include "styles/style_chat.h" @@ -57,7 +57,7 @@ void HistoryHider::paintEvent(QPaintEvent *e) { p.setFont(st::historyForwardChooseFont); auto w = st::historyForwardChooseMargins.left() + _chooseWidth + st::historyForwardChooseMargins.right(); auto h = st::historyForwardChooseMargins.top() + st::historyForwardChooseFont->height + st::historyForwardChooseMargins.bottom(); - App::roundRect(p, (width() - w) / 2, (height() - h) / 2, w, h, st::historyForwardChooseBg, ForwardCorners); + Ui::FillRoundRect(p, (width() - w) / 2, (height() - h) / 2, w, h, st::historyForwardChooseBg, Ui::ForwardCorners); p.setPen(st::historyForwardChooseFg); p.drawText(_box, _text, QTextOption(style::al_center)); diff --git a/Telegram/cmake/td_ui.cmake b/Telegram/cmake/td_ui.cmake index b1feecb12..12d26d2da 100644 --- a/Telegram/cmake/td_ui.cmake +++ b/Telegram/cmake/td_ui.cmake @@ -9,11 +9,25 @@ init_target(td_ui) add_library(tdesktop::td_ui ALIAS td_ui) include(lib_ui/cmake/generate_styles.cmake) +include(cmake/generate_numbers.cmake) set(style_files ui/td_common.style + ui/filter_icons.style ui/chat/chat.style + boxes/boxes.style dialogs/dialogs.style + chat_helpers/chat_helpers.style + calls/calls.style + export/view/export.style + info/info.style + intro/intro.style + media/player/media_player.style + passport/passport.style + profile/profile.style + settings/settings.style + media/view/media_view.style + overview/overview.style window/window.style ) @@ -25,14 +39,27 @@ set(dependent_style_files ) generate_styles(td_ui ${src_loc} "${style_files}" "${dependent_style_files}") +generate_numbers(td_ui ${res_loc}/numbers.txt) target_precompile_headers(td_ui PRIVATE ${src_loc}/ui/ui_pch.h) nice_target_sources(td_ui ${src_loc} PRIVATE ${style_files} + core/mime_type.cpp + core/mime_type.h + + ui/chat/attach/attach_album_thumbnail.cpp + ui/chat/attach/attach_album_thumbnail.h + ui/chat/attach/attach_album_preview.cpp + ui/chat/attach/attach_album_preview.h + ui/chat/attach/attach_common.h ui/chat/attach/attach_extensions.cpp ui/chat/attach/attach_extensions.h + ui/chat/attach/attach_prepare.cpp + ui/chat/attach/attach_prepare.h + ui/chat/attach/attach_single_file_preview.cpp + ui/chat/attach/attach_single_file_preview.h ui/chat/message_bar.cpp ui/chat/message_bar.h ui/chat/pinned_bar.cpp @@ -47,7 +74,11 @@ PRIVATE ui/text/text_options.h ui/toasts/common_toasts.cpp ui/toasts/common_toasts.h - + ui/cached_round_corners.cpp + ui/cached_round_corners.h + ui/grouped_layout.cpp + ui/grouped_layout.h + ui/ui_pch.h )