mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2026-07-25 06:54:43 +00:00
Send high quality as first-class option.
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="24px" height="24px" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<g stroke="none" fill="none" fill-rule="evenodd">
|
||||
<path d="M7.142857,5.4 L16.857143,5.4 C19.068542,5.4 20.857143,7.188601 20.857143,9.4 L20.857143,14.6 C20.857143,16.811399 19.068542,18.6 16.857143,18.6 L7.142857,18.6 C4.931458,18.6 3.142857,16.811399 3.142857,14.6 L3.142857,9.4 C3.142857,7.188601 4.931458,5.4 7.142857,5.4 Z M7.142857,6.6 C5.594716,6.6 4.342857,7.851859 4.342857,9.4 L4.342857,14.6 C4.342857,16.148141 5.594716,17.4 7.142857,17.4 L16.857143,17.4 C18.405284,17.4 19.657143,16.148141 19.657143,14.6 L19.657143,9.4 C19.657143,7.851859 18.405284,6.6 16.857143,6.6 L7.142857,6.6 Z M6.925,8.85 L8.225,8.85 L8.225,11.4 L10.225,11.4 L10.225,8.85 L11.525,8.85 L11.525,15.15 L10.225,15.15 L10.225,12.6 L8.225,12.6 L8.225,15.15 L6.925,15.15 L6.925,8.85 Z M12.725,8.85 L14.725,8.85 C16.466907,8.85 17.875,10.258093 17.875,12.0 C17.875,13.741907 16.466907,15.15 14.725,15.15 L12.725,15.15 L12.725,8.85 Z M14.025,10.05 L14.025,13.95 L14.725,13.95 C15.802321,13.95 16.675,13.077321 16.675,12.0 C16.675,10.922679 15.802321,10.05 14.725,10.05 L14.025,10.05 Z" fill="#FFFFFF" fill-rule="nonzero"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="24px" height="24px" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<g stroke="none" fill="none" fill-rule="evenodd">
|
||||
<path d="M7.142857,5.4 L16.857143,5.4 C19.068542,5.4 20.857143,7.188601 20.857143,9.4 L20.857143,14.6 C20.857143,16.811399 19.068542,18.6 16.857143,18.6 L7.142857,18.6 C4.931458,18.6 3.142857,16.811399 3.142857,14.6 L3.142857,9.4 C3.142857,7.188601 4.931458,5.4 7.142857,5.4 Z M7.142857,6.6 C5.594716,6.6 4.342857,7.851859 4.342857,9.4 L4.342857,14.6 C4.342857,16.148141 5.594716,17.4 7.142857,17.4 L16.857143,17.4 C18.405284,17.4 19.657143,16.148141 19.657143,14.6 L19.657143,9.4 C19.657143,7.851859 18.405284,6.6 16.857143,6.6 L7.142857,6.6 Z M10.8,9.35 C10.42,9.02 9.86,8.85 9.12,8.85 C8.38,8.85 7.78,9.06 7.35,9.46 C6.98,9.82 6.83,10.22 6.83,10.55 C6.83,11.05 7.06,11.46 7.5,11.78 C7.82,12.02 8.32,12.23 9.0,12.4 C9.56,12.54 9.88,12.65 10.02,12.73 C10.16,12.81 10.26,12.9 10.32,13.0 C10.38,13.1 10.41,13.2 10.41,13.3 C10.41,13.52 10.31,13.7 10.12,13.84 C9.92,13.99 9.62,14.07 9.22,14.07 C8.82,14.07 8.51,13.97 8.28,13.78 C8.06,13.58 7.92,13.3 7.84,12.93 L6.62,13.06 C6.7,13.74 6.96,14.26 7.4,14.62 C7.84,14.97 8.44,15.15 9.2,15.15 C9.96,15.15 10.56,14.94 11.0,14.54 C11.38,14.18 11.58,13.76 11.58,13.27 C11.58,12.77 11.36,12.36 10.92,12.04 C10.6,11.82 10.1,11.6 9.42,11.4 C8.78,11.22 8.42,11.07 8.24,10.92 C8.1,10.79 8.04,10.65 8.04,10.48 C8.04,10.31 8.11,10.17 8.26,10.06 C8.46,9.91 8.75,9.84 9.12,9.84 C9.48,9.84 9.74,9.92 9.92,10.06 C10.1,10.2 10.22,10.42 10.28,10.72 L11.48,10.66 C11.42,10.12 11.2,9.68 10.8,9.35 Z M12.828571,8.85 L14.828571,8.85 C16.570478,8.85 17.978571,10.258093 17.978571,12.0 C17.978571,13.741907 16.570478,15.15 14.828571,15.15 L12.828571,15.15 L12.828571,8.85 Z M14.128571,10.05 L14.128571,13.95 L14.828571,13.95 C15.905892,13.95 16.778571,13.077321 16.778571,12.0 C16.778571,10.922679 15.905892,10.05 14.828571,10.05 L14.128571,10.05 Z" fill="#FFFFFF" fill-rule="nonzero"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.0 KiB |
@@ -5171,8 +5171,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_send_grouped" = "Group items";
|
||||
"lng_send_compressed_one" = "Compress the image";
|
||||
"lng_send_compressed" = "Compress images";
|
||||
"lng_send_high_quality" = "Send in High Quality";
|
||||
"lng_send_as_documents_one" = "Send as a document";
|
||||
"lng_send_as_documents" = "Send as documents";
|
||||
"lng_send_standard_quality" = "Standard Quality";
|
||||
"lng_send_media_invalid_files" = "Sorry, no valid files found.";
|
||||
"lng_send_image" = "Send an image";
|
||||
"lng_send_file" = "Send as a file";
|
||||
|
||||
@@ -3820,6 +3820,7 @@ void ApiWrap::editMedia(
|
||||
.spoiler = false,
|
||||
.album = nullptr,
|
||||
.forceFile = false,
|
||||
.sendLargePhotos = false,
|
||||
.idOverride = 0,
|
||||
})
|
||||
: nullptr),
|
||||
@@ -3829,6 +3830,7 @@ void ApiWrap::editMedia(
|
||||
.spoiler = file.spoiler,
|
||||
.album = nullptr,
|
||||
.forceFile = forceFile,
|
||||
.sendLargePhotos = file.sendLargePhotos,
|
||||
.idOverride = 0,
|
||||
.displayName = file.displayName,
|
||||
}));
|
||||
@@ -3872,6 +3874,7 @@ void ApiWrap::sendFiles(
|
||||
.spoiler = false,
|
||||
.album = nullptr,
|
||||
.forceFile = false,
|
||||
.sendLargePhotos = false,
|
||||
.idOverride = 0,
|
||||
})
|
||||
: nullptr),
|
||||
@@ -3881,6 +3884,7 @@ void ApiWrap::sendFiles(
|
||||
.spoiler = file.spoiler,
|
||||
.album = album,
|
||||
.forceFile = forceFile,
|
||||
.sendLargePhotos = file.sendLargePhotos,
|
||||
.idOverride = 0,
|
||||
.displayName = file.displayName,
|
||||
}));
|
||||
|
||||
@@ -184,6 +184,7 @@ void EditPhotoImage(
|
||||
not_null<Window::SessionController*> controller,
|
||||
std::shared_ptr<Data::PhotoMedia> media,
|
||||
bool spoilered,
|
||||
int sideLimit,
|
||||
Fn<void(Ui::PreparedList)> done) {
|
||||
const auto large = media
|
||||
? media->image(Data::PhotoSize::Large)
|
||||
@@ -210,7 +211,6 @@ void EditPhotoImage(
|
||||
const auto image = std::get_if<ImageInfo>(&file.information->media);
|
||||
|
||||
image->modifications = mods;
|
||||
const auto sideLimit = PhotoSideLimit();
|
||||
Storage::UpdateImageDetails(file, previewWidth, sideLimit);
|
||||
done(std::move(list));
|
||||
};
|
||||
@@ -265,6 +265,7 @@ EditCaptionBox::EditCaptionBox(
|
||||
Expects(item->allowsEditMedia());
|
||||
|
||||
_asFile = !AlbumTypeCompressed(_albumType);
|
||||
_sendLargePhotos = Core::App().settings().sendFilesWay().sendLargePhotos();
|
||||
|
||||
_mediaEditManager.start(item, spoilered, invertCaption);
|
||||
|
||||
@@ -370,22 +371,26 @@ void EditCaptionBox::StartPhotoEdit(
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
EditPhotoImage(controller, media, spoilered, [=](
|
||||
Ui::PreparedList &&list) mutable {
|
||||
const auto item = session->data().message(itemId);
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
controller->show(Box<EditCaptionBox>(
|
||||
controller,
|
||||
item,
|
||||
std::move(text),
|
||||
suggest,
|
||||
spoilered,
|
||||
invertCaption,
|
||||
std::move(list),
|
||||
std::move(saved)));
|
||||
});
|
||||
EditPhotoImage(
|
||||
controller,
|
||||
media,
|
||||
spoilered,
|
||||
PhotoSideLimit(true),
|
||||
[=](Ui::PreparedList &&list) mutable {
|
||||
const auto item = session->data().message(itemId);
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
controller->show(Box<EditCaptionBox>(
|
||||
controller,
|
||||
item,
|
||||
std::move(text),
|
||||
suggest,
|
||||
spoilered,
|
||||
invertCaption,
|
||||
std::move(list),
|
||||
std::move(saved)));
|
||||
});
|
||||
}
|
||||
|
||||
void EditCaptionBox::showFinished() {
|
||||
@@ -419,10 +424,20 @@ void EditCaptionBox::prepare() {
|
||||
: _mediaEditManager.invertCaption()
|
||||
? SendMenu::CaptionState::Above
|
||||
: SendMenu::CaptionState::Below;
|
||||
result.photoQuality = !hasSendLargePhotosOption()
|
||||
? SendMenu::PhotoQualityState::None
|
||||
: _sendLargePhotos
|
||||
? SendMenu::PhotoQualityState::High
|
||||
: SendMenu::PhotoQualityState::Standard;
|
||||
return result;
|
||||
});
|
||||
const auto callback = [=](SendMenu::Action action, const auto &) {
|
||||
_mediaEditManager.apply(action);
|
||||
using Type = SendMenu::ActionType;
|
||||
switch (action.type) {
|
||||
case Type::PhotoQualityOn: _sendLargePhotos = true; break;
|
||||
case Type::PhotoQualityOff: _sendLargePhotos = false; break;
|
||||
default: _mediaEditManager.apply(action); break;
|
||||
}
|
||||
rebuildPreview();
|
||||
};
|
||||
SendMenu::SetupMenuAndShortcuts(
|
||||
@@ -496,6 +511,8 @@ void EditCaptionBox::rebuildPreview() {
|
||||
Ui::AttachControls::Type::EditOnly);
|
||||
_isPhoto = (media && media->isPhoto());
|
||||
if (media && !_asFile) {
|
||||
media->setSendWay(currentSendWay());
|
||||
media->setCanShowHighQualityBadge(file.canUseHighQualityPhoto());
|
||||
media->spoileredChanges(
|
||||
) | rpl::on_next([=](bool spoilered) {
|
||||
_mediaEditManager.apply({ .type = spoilered
|
||||
@@ -741,6 +758,20 @@ void EditCaptionBox::setupEditEventHandler() {
|
||||
}, &st::menuIconDraw);
|
||||
}
|
||||
if (!_asFile && (_isPhoto || _isVideo)) {
|
||||
if (hasSendLargePhotosOption()) {
|
||||
const auto enabled = _sendLargePhotos;
|
||||
(*menu)->addAction(
|
||||
(enabled
|
||||
? tr::lng_send_standard_quality(tr::now)
|
||||
: tr::lng_send_high_quality(tr::now)),
|
||||
[=] {
|
||||
_sendLargePhotos = !enabled;
|
||||
rebuildPreview();
|
||||
},
|
||||
enabled
|
||||
? &st::menuIconQualityStandard
|
||||
: &st::menuIconQualityHigh);
|
||||
}
|
||||
if (_preparedList.hasSpoilerMenu(!_asFile)) {
|
||||
const auto spoilered = hasSpoiler();
|
||||
auto text = spoilered
|
||||
@@ -803,12 +834,17 @@ void EditCaptionBox::setupPhotoEditorEventHandler() {
|
||||
controller->uiShow(),
|
||||
&_preparedList.files.front(),
|
||||
st::sendMediaPreviewSize,
|
||||
[=](bool ok) { if (ok) rebuildPreview(); });
|
||||
[=](bool ok) { if (ok) rebuildPreview(); },
|
||||
PhotoSideLimit(true));
|
||||
} else {
|
||||
EditPhotoImage(_controller, _photoMedia, hasSpoiler(), [=](
|
||||
Ui::PreparedList &&list) {
|
||||
setPreparedList(std::move(list));
|
||||
});
|
||||
EditPhotoImage(
|
||||
_controller,
|
||||
_photoMedia,
|
||||
hasSpoiler(),
|
||||
PhotoSideLimit(true),
|
||||
[=](Ui::PreparedList &&list) {
|
||||
setPreparedList(std::move(list));
|
||||
});
|
||||
}
|
||||
}, lifetime());
|
||||
}
|
||||
@@ -852,6 +888,7 @@ void EditCaptionBox::setupEditCoverHandler() {
|
||||
}
|
||||
rebuildPreview();
|
||||
}),
|
||||
PhotoSideLimit(true),
|
||||
video->thumbnail.size());
|
||||
};
|
||||
const auto checkResult = [=](const Ui::PreparedList &list) {
|
||||
@@ -1033,6 +1070,32 @@ bool EditCaptionBox::hasSpoiler() const {
|
||||
return _mediaEditManager.spoilered();
|
||||
}
|
||||
|
||||
bool EditCaptionBox::hasSendLargePhotosOption() const {
|
||||
const auto compressed = CanToggleCompressed(_albumType)
|
||||
? (!_asFile)
|
||||
: AlbumTypeCompressed(_albumType);
|
||||
return compressed
|
||||
&& !_preparedList.files.empty()
|
||||
&& _preparedList.hasSendLargePhotosOption(compressed);
|
||||
}
|
||||
|
||||
Ui::SendFilesWay EditCaptionBox::currentSendWay() const {
|
||||
auto way = Core::App().settings().sendFilesWay();
|
||||
way.setSendImagesAsPhotos(!_asFile);
|
||||
way.setSendLargePhotos(_sendLargePhotos);
|
||||
return way;
|
||||
}
|
||||
|
||||
void EditCaptionBox::saveSendWaySettings() {
|
||||
auto way = Core::App().settings().sendFilesWay();
|
||||
if (way.sendLargePhotos() == _sendLargePhotos) {
|
||||
return;
|
||||
}
|
||||
way.setSendLargePhotos(_sendLargePhotos);
|
||||
Core::App().settings().setSendFilesWay(way);
|
||||
Core::App().saveSettingsDelayed();
|
||||
}
|
||||
|
||||
void EditCaptionBox::captionResized() {
|
||||
updateBoxSize();
|
||||
resizeEvent(0);
|
||||
@@ -1141,6 +1204,7 @@ bool EditCaptionBox::validateLength(const QString &text) const {
|
||||
void EditCaptionBox::applyChanges() {
|
||||
if (!_preparedList.files.empty()) {
|
||||
_preparedList.files.front().spoiler = _mediaEditManager.spoilered();
|
||||
_preparedList.files.front().sendLargePhotos = _sendLargePhotos;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1192,6 +1256,7 @@ void EditCaptionBox::save() {
|
||||
const auto compressed = CanToggleCompressed(_albumType)
|
||||
? (!_asFile)
|
||||
: AlbumTypeCompressed(_albumType);
|
||||
saveSendWaySettings();
|
||||
_controller->session().api().editMedia(
|
||||
std::move(_preparedList),
|
||||
(compressed ? SendMediaType::Photo : SendMediaType::File),
|
||||
|
||||
@@ -112,6 +112,9 @@ private:
|
||||
|
||||
[[nodiscard]] int errorTopSkip() const;
|
||||
[[nodiscard]] bool hasSpoiler() const;
|
||||
[[nodiscard]] bool hasSendLargePhotosOption() const;
|
||||
[[nodiscard]] Ui::SendFilesWay currentSendWay() const;
|
||||
void saveSendWaySettings();
|
||||
|
||||
bool setPreparedList(Ui::PreparedList &&list);
|
||||
|
||||
@@ -147,6 +150,7 @@ private:
|
||||
bool _isPhoto = false;
|
||||
bool _isVideo = false;
|
||||
bool _asFile = false;
|
||||
bool _sendLargePhotos = false;
|
||||
|
||||
QString _error;
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "boxes/send_files_box.h"
|
||||
|
||||
#include "lang/lang_keys.h"
|
||||
#include "storage/localimageloader.h"
|
||||
#include "storage/localstorage.h"
|
||||
#include "storage/storage_media_prepare.h"
|
||||
#include "iv/iv_instance.h"
|
||||
@@ -386,6 +387,8 @@ SendFilesBox::Block::Block(
|
||||
: nullptr;
|
||||
if (media) {
|
||||
_isSingleMedia = true;
|
||||
media->setSendWay(way);
|
||||
media->setCanShowHighQualityBadge(first.canUseHighQualityPhoto());
|
||||
_preview.reset(media);
|
||||
} else {
|
||||
_preview.reset(Ui::CreateChild<Ui::SingleFilePreview>(
|
||||
@@ -674,6 +677,11 @@ Fn<SendMenu::Details()> SendFilesBox::prepareSendMenuDetails(
|
||||
: _invertCaption
|
||||
? SendMenu::CaptionState::Above
|
||||
: SendMenu::CaptionState::Below;
|
||||
result.photoQuality = !hasSendLargePhotosOption()
|
||||
? SendMenu::PhotoQualityState::None
|
||||
: way.sendLargePhotos()
|
||||
? SendMenu::PhotoQualityState::High
|
||||
: SendMenu::PhotoQualityState::Standard;
|
||||
result.price = canChangePrice()
|
||||
? _price.current()
|
||||
: std::optional<uint64>();
|
||||
@@ -688,6 +696,8 @@ auto SendFilesBox::prepareSendMenuCallback()
|
||||
switch (action.type) {
|
||||
case Type::CaptionDown: _invertCaption = false; break;
|
||||
case Type::CaptionUp: _invertCaption = true; break;
|
||||
case Type::PhotoQualityOn: setSendLargePhotos(true); break;
|
||||
case Type::PhotoQualityOff: setSendLargePhotos(false); break;
|
||||
case Type::SpoilerOn: toggleSpoilers(true); break;
|
||||
case Type::SpoilerOff: toggleSpoilers(false); break;
|
||||
case Type::ChangePrice: changePrice(); break;
|
||||
@@ -742,7 +752,7 @@ void SendFilesBox::enqueueNextPrepare() {
|
||||
_list.filesToProcess.pop_front();
|
||||
const auto weak = base::make_weak(this);
|
||||
_preparing = true;
|
||||
const auto sideLimit = PhotoSideLimit(); // Get on main thread.
|
||||
const auto sideLimit = PhotoSideLimit(_sendWay.current().sendLargePhotos());
|
||||
crl::async([weak, sideLimit, file = std::move(file)]() mutable {
|
||||
Storage::PrepareDetails(file, st::sendMediaPreviewSize, sideLimit);
|
||||
crl::on_main([weak, file = std::move(file)]() mutable {
|
||||
@@ -926,7 +936,9 @@ void SendFilesBox::refreshButtons() {
|
||||
bool SendFilesBox::hasSendMenu(const MenuDetails &details) const {
|
||||
return (details.type != SendMenu::Type::Disabled)
|
||||
|| (details.spoiler != SendMenu::SpoilerState::None)
|
||||
|| (details.caption != SendMenu::CaptionState::None);
|
||||
|| (details.caption != SendMenu::CaptionState::None)
|
||||
|| (details.photoQuality != SendMenu::PhotoQualityState::None)
|
||||
|| details.price.has_value();
|
||||
}
|
||||
|
||||
bool SendFilesBox::hasSpoilerMenu() const {
|
||||
@@ -934,6 +946,11 @@ bool SendFilesBox::hasSpoilerMenu() const {
|
||||
&& _list.hasSpoilerMenu(_sendWay.current().sendImagesAsPhotos());
|
||||
}
|
||||
|
||||
bool SendFilesBox::hasSendLargePhotosOption() const {
|
||||
return _list.hasSendLargePhotosOption(
|
||||
_sendWay.current().sendImagesAsPhotos());
|
||||
}
|
||||
|
||||
bool SendFilesBox::canChangePrice() const {
|
||||
const auto way = _sendWay.current();
|
||||
const auto broadcast = _toPeer->asBroadcast();
|
||||
@@ -964,6 +981,15 @@ void SendFilesBox::toggleSpoilers(bool enabled) {
|
||||
}
|
||||
}
|
||||
|
||||
void SendFilesBox::setSendLargePhotos(bool enabled) {
|
||||
auto way = _sendWay.current();
|
||||
if (way.sendLargePhotos() == enabled) {
|
||||
return;
|
||||
}
|
||||
way.setSendLargePhotos(enabled);
|
||||
_sendWay = way;
|
||||
}
|
||||
|
||||
void SendFilesBox::changePrice() {
|
||||
const auto weak = base::make_weak(this);
|
||||
const auto session = &_show->session();
|
||||
@@ -1249,7 +1275,8 @@ void SendFilesBox::pushBlock(int from, int till) {
|
||||
if (ok) {
|
||||
refreshAllAfterChanges(from);
|
||||
}
|
||||
});
|
||||
},
|
||||
PhotoSideLimit(true));
|
||||
};
|
||||
const auto replaceAttachment = [=, show = _show](int index) {
|
||||
applyBlockChanges();
|
||||
@@ -1358,6 +1385,7 @@ void SendFilesBox::pushBlock(int from, int till) {
|
||||
}
|
||||
refreshAllAfterChanges(from);
|
||||
}),
|
||||
PhotoSideLimit(true),
|
||||
video->thumbnail.size());
|
||||
};
|
||||
const auto checkResult = [=](const Ui::PreparedList &list) {
|
||||
@@ -2171,14 +2199,18 @@ void SendFilesBox::setInnerFocus() {
|
||||
}
|
||||
}
|
||||
|
||||
void SendFilesBox::saveSendWaySettings() {
|
||||
void SendFilesBox::saveSendWaySettings(bool rememberAll) {
|
||||
auto way = _sendWay.current();
|
||||
auto oldWay = Core::App().settings().sendFilesWay();
|
||||
if (_groupFiles->isHidden()) {
|
||||
if (!rememberAll) {
|
||||
way.setGroupFiles(oldWay.groupFiles());
|
||||
way.setSendImagesAsPhotos(oldWay.sendImagesAsPhotos());
|
||||
} else if (_groupFiles->isHidden()) {
|
||||
way.setGroupFiles(oldWay.groupFiles());
|
||||
}
|
||||
if (_list.overrideSendImagesAsPhotos == way.sendImagesAsPhotos()
|
||||
|| _sendImagesAsPhotos->isHidden()) {
|
||||
if (rememberAll
|
||||
&& (_list.overrideSendImagesAsPhotos == way.sendImagesAsPhotos()
|
||||
|| _sendImagesAsPhotos->isHidden())) {
|
||||
way.setSendImagesAsPhotos(oldWay.sendImagesAsPhotos());
|
||||
}
|
||||
if (way != oldWay) {
|
||||
@@ -2208,6 +2240,7 @@ void SendFilesBox::send(
|
||||
auto child = _sendMenuDetails();
|
||||
child.spoiler = SendMenu::SpoilerState::None;
|
||||
child.caption = SendMenu::CaptionState::None;
|
||||
child.photoQuality = SendMenu::PhotoQualityState::None;
|
||||
child.price = std::nullopt;
|
||||
return SendMenu::DefaultCallback(_show, sendCallback())(
|
||||
{ .type = SendMenu::ActionType::Schedule },
|
||||
@@ -2220,20 +2253,20 @@ void SendFilesBox::send(
|
||||
return;
|
||||
}
|
||||
|
||||
if (_wayRemember && _wayRemember->checked()) {
|
||||
saveSendWaySettings();
|
||||
}
|
||||
const auto way = _sendWay.current();
|
||||
|
||||
for (auto &item : _list.files) {
|
||||
item.spoiler = false;
|
||||
}
|
||||
applyBlockChanges();
|
||||
for (auto &item : _list.files) {
|
||||
item.sendLargePhotos = way.sendLargePhotos();
|
||||
}
|
||||
|
||||
Storage::ApplyModifications(_list);
|
||||
|
||||
_confirmed = true;
|
||||
if (_confirmedCallback) {
|
||||
const auto way = _sendWay.current();
|
||||
auto caption = fieldText();
|
||||
if (!validateLength(caption.text)) {
|
||||
return;
|
||||
@@ -2244,6 +2277,7 @@ void SendFilesBox::send(
|
||||
return;
|
||||
}
|
||||
}
|
||||
saveSendWaySettings(_wayRemember && _wayRemember->checked());
|
||||
options.invertCaption = _invertCaption;
|
||||
options.price = hasPrice() ? _price.current() : 0;
|
||||
if (options.price > 0) {
|
||||
|
||||
@@ -211,10 +211,12 @@ private:
|
||||
void addMenuButton();
|
||||
void applyBlockChanges();
|
||||
void toggleSpoilers(bool enabled);
|
||||
void setSendLargePhotos(bool enabled);
|
||||
void changePrice();
|
||||
|
||||
[[nodiscard]] bool canChangePrice() const;
|
||||
[[nodiscard]] bool hasPrice() const;
|
||||
[[nodiscard]] bool hasSendLargePhotosOption() const;
|
||||
void refreshPriceTag();
|
||||
[[nodiscard]] QImage preparePriceTagBg(QSize size) const;
|
||||
|
||||
@@ -235,7 +237,7 @@ private:
|
||||
void send(Api::SendOptions options, bool ctrlShiftEnter = false);
|
||||
[[nodiscard]] Fn<void(Api::SendOptions)> sendCallback();
|
||||
void captionResized();
|
||||
void saveSendWaySettings();
|
||||
void saveSendWaySettings(bool rememberAll);
|
||||
|
||||
void setupDragArea();
|
||||
void refreshTitleText();
|
||||
|
||||
@@ -72,6 +72,8 @@ ComposeIcons {
|
||||
menuSpoilerOff: icon;
|
||||
menuBelow: icon;
|
||||
menuAbove: icon;
|
||||
menuQualityHigh: icon;
|
||||
menuQualityStandard: icon;
|
||||
menuPrice: icon;
|
||||
menuEditStars: icon;
|
||||
|
||||
@@ -242,6 +244,7 @@ ComposeControls {
|
||||
tabbedHeightMax: pixels;
|
||||
record: RecordBar;
|
||||
files: ComposeFiles;
|
||||
photoQualityBadgeOuterSkip: pixels;
|
||||
premium: PremiumLimits;
|
||||
boxField: InputField;
|
||||
restrictionLabel: FlatLabel;
|
||||
@@ -657,6 +660,8 @@ defaultComposeIcons: ComposeIcons {
|
||||
menuSpoilerOff: menuIconSpoilerOff;
|
||||
menuBelow: menuIconBelow;
|
||||
menuAbove: menuIconAbove;
|
||||
menuQualityHigh: menuIconQualityHigh;
|
||||
menuQualityStandard: menuIconQualityStandard;
|
||||
menuPrice: menuIconEarn;
|
||||
|
||||
stripBubble: icon{
|
||||
@@ -1511,6 +1516,7 @@ defaultComposeControls: ComposeControls {
|
||||
tabbedHeightMax: emojiPanMaxHeight;
|
||||
record: defaultRecordBar;
|
||||
files: defaultComposeFiles;
|
||||
photoQualityBadgeOuterSkip: 3px;
|
||||
premium: defaultPremiumLimits;
|
||||
boxField: defaultInputField;
|
||||
restrictionLabel: defaultRestrictionLabel;
|
||||
|
||||
@@ -27,6 +27,7 @@ void OpenWithPreparedFile(
|
||||
not_null<Ui::PreparedFile*> file,
|
||||
int previewWidth,
|
||||
Fn<void(bool ok)> &&doneCallback,
|
||||
int sideLimit,
|
||||
QSize exactSize) {
|
||||
using ImageInfo = Ui::PreparedFileInformation::Image;
|
||||
const auto image = std::get_if<ImageInfo>(&file->information->media);
|
||||
@@ -42,7 +43,7 @@ void OpenWithPreparedFile(
|
||||
return;
|
||||
}
|
||||
|
||||
const auto sideLimit = PhotoSideLimit();
|
||||
sideLimit = sideLimit ? sideLimit : PhotoSideLimit(true);
|
||||
const auto accepted = std::make_shared<bool>();
|
||||
auto callback = [=](const PhotoModifications &mods) {
|
||||
*accepted = true;
|
||||
|
||||
@@ -35,6 +35,7 @@ void OpenWithPreparedFile(
|
||||
not_null<Ui::PreparedFile*> file,
|
||||
int previewWidth,
|
||||
Fn<void(bool ok)> &&doneCallback,
|
||||
int sideLimit = 0,
|
||||
QSize exactSize = {});
|
||||
|
||||
void PrepareProfilePhoto(
|
||||
|
||||
@@ -659,6 +659,8 @@ storiesEmojiPan: EmojiPan(defaultEmojiPan) {
|
||||
menuSpoilerOff: icon {{ "menu/spoiler_off", storiesComposeWhiteText }};
|
||||
menuBelow: icon {{ "menu/link_below", storiesComposeWhiteText }};
|
||||
menuAbove: icon {{ "menu/link_above", storiesComposeWhiteText }};
|
||||
menuQualityHigh: icon {{ "menu/quality_hd", storiesComposeWhiteText }};
|
||||
menuQualityStandard: icon {{ "menu/quality_sd", storiesComposeWhiteText }};
|
||||
menuPrice: icon {{ "menu/earn", storiesComposeWhiteText }};
|
||||
menuEditStars: icon {{ "chat/input_paid", storiesComposeWhiteText, point(-8px, -8px) }};
|
||||
|
||||
|
||||
@@ -722,6 +722,7 @@ FillMenuResult FillSendMenu(
|
||||
const auto empty = !sending
|
||||
&& (details.spoiler == SpoilerState::None)
|
||||
&& (details.caption == CaptionState::None)
|
||||
&& (details.photoQuality == PhotoQualityState::None)
|
||||
&& !details.price.has_value();
|
||||
if (empty || !action) {
|
||||
return FillMenuResult::Skipped;
|
||||
@@ -764,9 +765,24 @@ FillMenuResult FillSendMenu(
|
||||
if ((type != Type::Disabled)
|
||||
&& ((details.spoiler != SpoilerState::None)
|
||||
|| (details.caption != CaptionState::None)
|
||||
|| (details.photoQuality != PhotoQualityState::None)
|
||||
|| details.price.has_value())) {
|
||||
menu->addSeparator(&st::expandedMenuSeparator);
|
||||
}
|
||||
if (details.photoQuality != PhotoQualityState::None) {
|
||||
const auto high = (details.photoQuality == PhotoQualityState::High);
|
||||
menu->addAction(
|
||||
(high
|
||||
? tr::lng_send_standard_quality(tr::now)
|
||||
: tr::lng_send_high_quality(tr::now)),
|
||||
[=] { action({ .type = high
|
||||
? ActionType::PhotoQualityOff
|
||||
: ActionType::PhotoQualityOn
|
||||
}, details); },
|
||||
(high
|
||||
? &icons.menuQualityStandard
|
||||
: &icons.menuQualityHigh));
|
||||
}
|
||||
if (details.spoiler != SpoilerState::None) {
|
||||
const auto spoilered = (details.spoiler == SpoilerState::Enabled);
|
||||
menu->addAction(
|
||||
|
||||
@@ -51,10 +51,17 @@ enum class CaptionState : uchar {
|
||||
Above,
|
||||
};
|
||||
|
||||
enum class PhotoQualityState : uchar {
|
||||
None,
|
||||
Standard,
|
||||
High,
|
||||
};
|
||||
|
||||
struct Details {
|
||||
Type type = Type::Disabled;
|
||||
SpoilerState spoiler = SpoilerState::None;
|
||||
CaptionState caption = CaptionState::None;
|
||||
PhotoQualityState photoQuality = PhotoQualityState::None;
|
||||
TextWithTags commentPreview;
|
||||
QString commentStreamerName;
|
||||
std::optional<uint64> price;
|
||||
@@ -75,6 +82,8 @@ enum class ActionType : uchar {
|
||||
SpoilerOff,
|
||||
CaptionUp,
|
||||
CaptionDown,
|
||||
PhotoQualityOn,
|
||||
PhotoQualityOff,
|
||||
ChangePrice,
|
||||
};
|
||||
struct Action {
|
||||
|
||||
@@ -39,7 +39,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "window/window_session_controller.h"
|
||||
#include "window/window_controller.h"
|
||||
#include "window/notifications_manager.h"
|
||||
#include "storage/localimageloader.h"
|
||||
#include "info/info_flexible_scroll.h"
|
||||
#include "chat_helpers/stickers_list_widget.h"
|
||||
#include "styles/style_settings.h"
|
||||
@@ -212,7 +211,6 @@ void SetupExperimental(
|
||||
addToggle(Info::Profile::kOptionShowChannelJoinedBelowAbout);
|
||||
addToggle(Ui::kOptionUseSmallMsgBubbleRadius);
|
||||
addToggle(Media::Player::kOptionDisableAutoplayNext);
|
||||
addToggle(kOptionSendLargePhotos);
|
||||
addToggle(Webview::kOptionWebviewDebugEnabled);
|
||||
addToggle(Webview::kOptionWebviewLegacyEdge);
|
||||
addToggle(kOptionAutoScrollInactiveChat);
|
||||
|
||||
@@ -12,9 +12,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "data/data_document.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_user.h"
|
||||
#include "core/application.h"
|
||||
#include "core/core_settings.h"
|
||||
#include "core/file_utilities.h"
|
||||
#include "core/mime_type.h"
|
||||
#include "base/options.h"
|
||||
#include "base/unixtime.h"
|
||||
#include "base/random.h"
|
||||
#include "editor/scene/scene_item_sticker.h"
|
||||
@@ -51,13 +52,6 @@ constexpr auto kRecompressAfterBpp = 4;
|
||||
|
||||
using Ui::ValidateThumbDimensions;
|
||||
|
||||
base::options::toggle SendLargePhotos({
|
||||
.id = kOptionSendLargePhotos,
|
||||
.name = "Send large photos",
|
||||
.description = "Increase the side limit on compressed images to 2560px.",
|
||||
});
|
||||
std::atomic<bool> SendLargePhotosAtomic/* = false*/;
|
||||
|
||||
struct PreparedFileThumbnail {
|
||||
uint64 id = 0;
|
||||
QString name;
|
||||
@@ -206,20 +200,15 @@ struct PreparedFileThumbnail {
|
||||
return result;
|
||||
}
|
||||
|
||||
[[nodiscard]] int PhotoSideLimit(bool large) {
|
||||
} // namespace
|
||||
|
||||
int PhotoSideLimit(bool large) {
|
||||
return large ? 2560 : 1280;
|
||||
}
|
||||
|
||||
[[nodiscard]] int PhotoSideLimitAtomic() {
|
||||
return PhotoSideLimit(SendLargePhotosAtomic.load());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
const char kOptionSendLargePhotos[] = "send-large-photos";
|
||||
|
||||
int PhotoSideLimit() {
|
||||
return PhotoSideLimit(SendLargePhotos.value());
|
||||
return PhotoSideLimit(
|
||||
Core::App().settings().sendFilesWay().sendLargePhotos());
|
||||
}
|
||||
|
||||
TaskQueue::TaskQueue(crl::time stopTimeoutMs) {
|
||||
@@ -480,13 +469,12 @@ FileLoadTask::FileLoadTask(Args &&args)
|
||||
, _type(args.type)
|
||||
, _caption(std::move(args.caption))
|
||||
, _spoiler(args.spoiler)
|
||||
, _forceFile(args.forceFile) {
|
||||
, _forceFile(args.forceFile)
|
||||
, _sendLargePhotos(args.sendLargePhotos) {
|
||||
Expects(_to.options.scheduled
|
||||
|| _to.options.shortcutId
|
||||
|| !_to.replaceMediaOf
|
||||
|| IsServerMsgId(_to.replaceMediaOf));
|
||||
|
||||
SendLargePhotosAtomic = SendLargePhotos.value();
|
||||
}
|
||||
|
||||
FileLoadTask::FileLoadTask(VoiceArgs &&args)
|
||||
@@ -944,7 +932,7 @@ void FileLoadTask::process(ProcessArgs &&args) {
|
||||
}
|
||||
auto medium = (w > 320 || h > 320) ? fullimage.scaled(320, 320, Qt::KeepAspectRatio, Qt::SmoothTransformation) : fullimage;
|
||||
|
||||
const auto limit = PhotoSideLimitAtomic();
|
||||
const auto limit = PhotoSideLimit(_sendLargePhotos);
|
||||
const auto downscaled = (w > limit || h > limit);
|
||||
auto full = downscaled ? fullimage.scaled(limit, limit, Qt::KeepAspectRatio, Qt::SmoothTransformation) : fullimage;
|
||||
if (downscaled) {
|
||||
|
||||
@@ -24,8 +24,7 @@ constexpr auto kFileSizeLimit = 2'000 * int64(1024 * 1024);
|
||||
// Load files up to 4'000 MB.
|
||||
constexpr auto kFileSizePremiumLimit = 4'000 * int64(1024 * 1024);
|
||||
|
||||
extern const char kOptionSendLargePhotos[];
|
||||
|
||||
[[nodiscard]] int PhotoSideLimit(bool large);
|
||||
[[nodiscard]] int PhotoSideLimit();
|
||||
|
||||
enum class SendMediaType {
|
||||
@@ -232,6 +231,7 @@ public:
|
||||
bool spoiler = false;
|
||||
std::shared_ptr<SendingAlbum> album;
|
||||
bool forceFile = false;
|
||||
bool sendLargePhotos = false;
|
||||
uint64 idOverride = 0;
|
||||
QString displayName;
|
||||
};
|
||||
@@ -303,6 +303,7 @@ private:
|
||||
TextWithTags _caption;
|
||||
bool _spoiler = false;
|
||||
bool _forceFile = false;
|
||||
bool _sendLargePhotos = false;
|
||||
|
||||
std::shared_ptr<FilePrepareResult> _result;
|
||||
|
||||
|
||||
@@ -81,6 +81,11 @@ void AbstractSingleMediaPreview::setSpoiler(bool spoiler) {
|
||||
update();
|
||||
}
|
||||
|
||||
void AbstractSingleMediaPreview::setCanShowHighQualityBadge(bool value) {
|
||||
_canShowHighQualityBadge = value;
|
||||
update();
|
||||
}
|
||||
|
||||
bool AbstractSingleMediaPreview::hasSpoiler() const {
|
||||
return _spoiler != nullptr;
|
||||
}
|
||||
@@ -249,6 +254,12 @@ void AbstractSingleMediaPreview::paintEvent(QPaintEvent *e) {
|
||||
auto icon = &st::historyFileInPlay;
|
||||
icon->paintInCenter(p, inner);
|
||||
}
|
||||
if (_canShowHighQualityBadge && _sendWay.sendLargePhotos()) {
|
||||
PaintHighQualityBadge(
|
||||
p,
|
||||
_st,
|
||||
QRect(_previewLeft, _previewTop, _previewWidth, _previewHeight));
|
||||
}
|
||||
}
|
||||
|
||||
void AbstractSingleMediaPreview::mousePressEvent(QMouseEvent *e) {
|
||||
|
||||
@@ -42,6 +42,7 @@ public:
|
||||
[[nodiscard]] bool isPhoto() const;
|
||||
|
||||
void setSpoiler(bool spoiler);
|
||||
void setCanShowHighQualityBadge(bool value);
|
||||
[[nodiscard]] bool hasSpoiler() const;
|
||||
[[nodiscard]] bool canHaveSpoiler() const;
|
||||
[[nodiscard]] rpl::producer<bool> spoileredChanges() const;
|
||||
@@ -87,6 +88,7 @@ private:
|
||||
|
||||
std::unique_ptr<SpoilerAnimation> _spoiler;
|
||||
rpl::event_stream<bool> _spoileredChanges;
|
||||
bool _canShowHighQualityBadge = false;
|
||||
|
||||
const int _minThumbH;
|
||||
const base::unique_qptr<AttachControlsWidget> _controls;
|
||||
|
||||
@@ -381,11 +381,23 @@ void AlbumPreview::paintAlbum(Painter &p) const {
|
||||
const auto top = contentTop();
|
||||
for (const auto &thumb : _thumbs) {
|
||||
if (thumb.get() != _paintedAbove) {
|
||||
thumb->paintInAlbum(p, left, top, shrink, moveProgress);
|
||||
thumb->paintInAlbum(
|
||||
p,
|
||||
left,
|
||||
top,
|
||||
shrink,
|
||||
moveProgress,
|
||||
_sendWay.sendLargePhotos());
|
||||
}
|
||||
}
|
||||
if (_paintedAbove) {
|
||||
_paintedAbove->paintInAlbum(p, left, top, shrink, moveProgress);
|
||||
_paintedAbove->paintInAlbum(
|
||||
p,
|
||||
left,
|
||||
top,
|
||||
shrink,
|
||||
moveProgress,
|
||||
_sendWay.sendLargePhotos());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -403,7 +415,12 @@ void AlbumPreview::paintPhotos(Painter &p, QRect clip) const {
|
||||
} else if (bottom <= clip.y()) {
|
||||
continue;
|
||||
}
|
||||
thumb->paintPhoto(p, left, top, outerWidth);
|
||||
thumb->paintPhoto(
|
||||
p,
|
||||
left,
|
||||
top,
|
||||
outerWidth,
|
||||
_sendWay.sendLargePhotos());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -698,7 +715,7 @@ QImage AlbumPreview::generatePriceTagBackground() const {
|
||||
p.translate(geometry.center());
|
||||
p.scale(wscale, hscale);
|
||||
p.translate(-geometry.center());
|
||||
thumb->paintInAlbum(p, 0, 0, 1., 1.);
|
||||
thumb->paintInAlbum(p, 0, 0, 1., 1., _sendWay.sendLargePhotos());
|
||||
p.restore();
|
||||
}
|
||||
p.end();
|
||||
|
||||
@@ -42,6 +42,7 @@ AlbumThumbnail::AlbumThumbnail(
|
||||
, _shrinkSize(int(std::ceil(st::roundRadiusLarge / 1.4)))
|
||||
, _isPhoto(file.type == PreparedFile::Type::Photo)
|
||||
, _isVideo(file.type == PreparedFile::Type::Video)
|
||||
, _canShowHighQualityBadge(file.canUseHighQualityPhoto())
|
||||
, _isCompressedSticker(Core::IsMimeSticker(file.information->filemime))
|
||||
, _repaint(std::move(repaint))
|
||||
, _repaintRect(std::move(repaintRect)) {
|
||||
@@ -250,7 +251,8 @@ void AlbumThumbnail::paintInAlbum(
|
||||
int left,
|
||||
int top,
|
||||
float64 shrinkProgress,
|
||||
float64 moveProgress) {
|
||||
float64 moveProgress,
|
||||
bool showHighQualityBadge) {
|
||||
const auto shrink = anim::interpolate(0, _shrinkSize, shrinkProgress);
|
||||
_lastShrinkValue = shrink;
|
||||
const auto geometry = countCurrentGeometry(
|
||||
@@ -314,6 +316,9 @@ void AlbumThumbnail::paintInAlbum(
|
||||
geometry,
|
||||
shrinkProgress);
|
||||
_lastRectOfModify = geometry;
|
||||
if (showHighQualityBadge && _canShowHighQualityBadge) {
|
||||
PaintHighQualityBadge(p, _st, paintedTo);
|
||||
}
|
||||
}
|
||||
|
||||
void AlbumThumbnail::paintPlayVideo(QPainter &p, QRect geometry) {
|
||||
@@ -476,7 +481,12 @@ void AlbumThumbnail::drawSimpleFrame(QPainter &p, QRect to, QSize size) const {
|
||||
}
|
||||
}
|
||||
|
||||
void AlbumThumbnail::paintPhoto(Painter &p, int left, int top, int outerWidth) {
|
||||
void AlbumThumbnail::paintPhoto(
|
||||
Painter &p,
|
||||
int left,
|
||||
int top,
|
||||
int outerWidth,
|
||||
bool showHighQualityBadge) {
|
||||
const auto size = _photo.size() / style::DevicePixelRatio();
|
||||
if (_spoiler && _photoBlurred.isNull()) {
|
||||
_photoBlurred = BlurredPreviewFromPixmap(
|
||||
@@ -515,6 +525,9 @@ void AlbumThumbnail::paintPhoto(Painter &p, int left, int top, int outerWidth) {
|
||||
0);
|
||||
|
||||
_lastRectOfModify = QRect(topLeft, size);
|
||||
if (showHighQualityBadge && _canShowHighQualityBadge) {
|
||||
PaintHighQualityBadge(p, _st, rect);
|
||||
}
|
||||
}
|
||||
|
||||
void AlbumThumbnail::paintFile(
|
||||
@@ -525,7 +538,7 @@ void AlbumThumbnail::paintFile(
|
||||
|
||||
if (isCompressedSticker()) {
|
||||
auto spoiler = base::take(_spoiler);
|
||||
paintPhoto(p, left, top, outerWidth);
|
||||
paintPhoto(p, left, top, outerWidth, false);
|
||||
_spoiler = base::take(spoiler);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -53,8 +53,14 @@ public:
|
||||
int left,
|
||||
int top,
|
||||
float64 shrinkProgress,
|
||||
float64 moveProgress);
|
||||
void paintPhoto(Painter &p, int left, int top, int outerWidth);
|
||||
float64 moveProgress,
|
||||
bool showHighQualityBadge);
|
||||
void paintPhoto(
|
||||
Painter &p,
|
||||
int left,
|
||||
int top,
|
||||
int outerWidth,
|
||||
bool showHighQualityBadge);
|
||||
void paintFile(Painter &p, int left, int top, int outerWidth);
|
||||
|
||||
[[nodiscard]] QRect geometry() const;
|
||||
@@ -94,6 +100,7 @@ private:
|
||||
const int _shrinkSize;
|
||||
const bool _isPhoto;
|
||||
const bool _isVideo;
|
||||
const bool _canShowHighQualityBadge;
|
||||
QPixmap _albumImage;
|
||||
QPixmap _albumImageBlurred;
|
||||
QImage _albumCache;
|
||||
|
||||
@@ -12,8 +12,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
#include "ui/chat/attach/attach_send_files_way.h"
|
||||
#include "ui/image/image_prepare.h"
|
||||
#include "ui/painter.h"
|
||||
#include "ui/ui_utility.h"
|
||||
#include "core/mime_type.h"
|
||||
#include "styles/style_chat.h"
|
||||
#include "styles/style_chat_helpers.h"
|
||||
#include "styles/style_media_player.h"
|
||||
|
||||
#include <QFileInfo>
|
||||
|
||||
@@ -21,6 +25,7 @@ namespace Ui {
|
||||
namespace {
|
||||
|
||||
constexpr auto kMaxAlbumCount = 10;
|
||||
constexpr auto kStandardPhotoSideLimit = 1280;
|
||||
|
||||
struct GroupRange {
|
||||
int from = 0;
|
||||
@@ -32,6 +37,69 @@ struct GroupRange {
|
||||
}
|
||||
};
|
||||
|
||||
struct HighQualityBadgeCache {
|
||||
QRgb bg = 0;
|
||||
QRgb fg = 0;
|
||||
qreal ratio = 0.;
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
QImage image;
|
||||
};
|
||||
|
||||
[[nodiscard]] const QImage &HighQualityBadgeImage(
|
||||
const style::ComposeControls &st) {
|
||||
static auto cache = HighQualityBadgeCache();
|
||||
const auto text = u"HD"_q;
|
||||
const auto &font = st::mediaPlayerSpeedButton.font;
|
||||
const auto xpadding = style::ConvertScale(2.);
|
||||
const auto ypadding = 0;
|
||||
const auto stroke = style::ConvertScaleExact(1.);
|
||||
const auto width = font->width(text);
|
||||
const auto height = font->height;
|
||||
const auto ratio = style::DevicePixelRatio();
|
||||
const auto bg = st::roundedBg->c.rgba();
|
||||
const auto fg = st::roundedFg->c.rgba();
|
||||
if (cache.image.isNull()
|
||||
|| (cache.bg != bg)
|
||||
|| (cache.fg != fg)
|
||||
|| (cache.ratio != ratio)
|
||||
|| (cache.width != width)
|
||||
|| (cache.height != height)) {
|
||||
cache.bg = bg;
|
||||
cache.fg = fg;
|
||||
cache.ratio = ratio;
|
||||
cache.width = width;
|
||||
cache.height = height;
|
||||
cache.image = QImage(
|
||||
(width + 2 * xpadding + stroke) * ratio,
|
||||
(height + 2 * ypadding + stroke) * ratio,
|
||||
QImage::Format_ARGB32_Premultiplied);
|
||||
cache.image.setDevicePixelRatio(ratio);
|
||||
cache.image.fill(Qt::transparent);
|
||||
auto painter = QPainter(&cache.image);
|
||||
auto hq = PainterHighQualityEnabler(painter);
|
||||
painter.setCompositionMode(QPainter::CompositionMode_Source);
|
||||
painter.setPen(QPen(Qt::transparent, stroke));
|
||||
painter.setBrush(st::roundedBg);
|
||||
painter.setFont(font);
|
||||
painter.drawRoundedRect(
|
||||
QRectF(
|
||||
0,
|
||||
0,
|
||||
width + 2 * xpadding + stroke,
|
||||
height + 2 * ypadding + stroke),
|
||||
height / 3.,
|
||||
height / 3.);
|
||||
painter.setPen(st::roundedFg);
|
||||
painter.drawText(
|
||||
QPointF(
|
||||
xpadding + stroke / 2.,
|
||||
ypadding + font->ascent + stroke / 2.),
|
||||
text);
|
||||
}
|
||||
return cache.image;
|
||||
}
|
||||
|
||||
[[nodiscard]] AlbumType GroupTypeForFile(
|
||||
PreparedFile::Type type,
|
||||
bool groupFiles,
|
||||
@@ -132,6 +200,14 @@ bool PreparedFile::isGifv() const {
|
||||
&& v::get<Video>(information->media).isGifv;
|
||||
}
|
||||
|
||||
bool PreparedFile::canUseHighQualityPhoto() const {
|
||||
return (type == PreparedFile::Type::Photo)
|
||||
&& (information != nullptr)
|
||||
&& !isSticker()
|
||||
&& ((originalDimensions.width() > kStandardPhotoSideLimit)
|
||||
|| (originalDimensions.height() > kStandardPhotoSideLimit));
|
||||
}
|
||||
|
||||
AlbumType PreparedFile::albumType(bool sendImagesAsPhotos) const {
|
||||
switch (type) {
|
||||
case Type::Photo:
|
||||
@@ -332,6 +408,11 @@ bool PreparedList::hasSpoilerMenu(bool compress) const {
|
||||
return allAreVideo || (allAreMedia && compress);
|
||||
}
|
||||
|
||||
bool PreparedList::hasSendLargePhotosOption(bool compress) const {
|
||||
return compress
|
||||
&& ranges::any_of(files, &PreparedFile::canUseHighQualityPhoto);
|
||||
}
|
||||
|
||||
std::shared_ptr<PreparedBundle> PrepareFilesBundle(
|
||||
std::vector<PreparedGroup> groups,
|
||||
SendFilesWay way,
|
||||
@@ -418,4 +499,25 @@ QPixmap BlurredPreviewFromPixmap(QPixmap pixmap, RectParts corners) {
|
||||
{ .options = RoundOptions(ImageRoundRadius::Large, corners) }));
|
||||
}
|
||||
|
||||
void PaintHighQualityBadge(
|
||||
QPainter &p,
|
||||
const style::ComposeControls &st,
|
||||
QRect rect,
|
||||
RectPart origin) {
|
||||
const auto outerSkip = st.photoQualityBadgeOuterSkip;
|
||||
const auto &badge = HighQualityBadgeImage(st);
|
||||
const auto size = badge.size() / badge.devicePixelRatio();
|
||||
const auto left = (origin == RectPart::TopLeft)
|
||||
|| (origin == RectPart::BottomLeft);
|
||||
const auto top = (origin == RectPart::TopLeft)
|
||||
|| (origin == RectPart::TopRight);
|
||||
const auto x = left
|
||||
? (rect.x() + outerSkip)
|
||||
: (rect.x() + rect.width() - size.width() - outerSkip);
|
||||
const auto y = top
|
||||
? (rect.y() + outerSkip)
|
||||
: (rect.y() + rect.height() - size.height() - outerSkip);
|
||||
p.drawImage(QPointF(x, y), badge);
|
||||
}
|
||||
|
||||
} // namespace Ui
|
||||
|
||||
@@ -14,6 +14,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include <QtCore/QSemaphore>
|
||||
#include <deque>
|
||||
|
||||
class QPainter;
|
||||
|
||||
namespace style {
|
||||
struct ComposeControls;
|
||||
} // namespace style
|
||||
|
||||
namespace Ui {
|
||||
|
||||
class RpWidget;
|
||||
@@ -77,6 +83,7 @@ struct PreparedFile {
|
||||
[[nodiscard]] bool isSticker() const;
|
||||
[[nodiscard]] bool isVideoFile() const;
|
||||
[[nodiscard]] bool isGifv() const;
|
||||
[[nodiscard]] bool canUseHighQualityPhoto() const;
|
||||
|
||||
QString path;
|
||||
QString displayName;
|
||||
@@ -90,6 +97,7 @@ struct PreparedFile {
|
||||
QSize originalDimensions;
|
||||
Type type = Type::File;
|
||||
bool spoiler = false;
|
||||
bool sendLargePhotos = false;
|
||||
};
|
||||
|
||||
[[nodiscard]] bool CanBeInAlbumType(PreparedFile::Type type, AlbumType album);
|
||||
@@ -133,6 +141,7 @@ struct PreparedList {
|
||||
[[nodiscard]] bool canHaveEditorHintLabel() const;
|
||||
[[nodiscard]] bool hasSticker() const;
|
||||
[[nodiscard]] bool hasSpoilerMenu(bool compress) const;
|
||||
[[nodiscard]] bool hasSendLargePhotosOption(bool compress) const;
|
||||
|
||||
Error error = Error::None;
|
||||
QString errorData;
|
||||
@@ -171,4 +180,10 @@ struct PreparedBundle {
|
||||
QPixmap pixmap,
|
||||
RectParts corners);
|
||||
|
||||
void PaintHighQualityBadge(
|
||||
QPainter &p,
|
||||
const style::ComposeControls &st,
|
||||
QRect rect,
|
||||
RectPart origin = RectPart::BottomLeft);
|
||||
|
||||
} // namespace Ui
|
||||
|
||||
@@ -8,6 +8,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/chat/attach/attach_send_files_way.h"
|
||||
|
||||
namespace Ui {
|
||||
namespace {
|
||||
|
||||
constexpr auto kSerializedSendLargePhotos = int32(4);
|
||||
|
||||
} // namespace
|
||||
|
||||
void SendFilesWay::setSendImagesAsPhotos(bool value) {
|
||||
if (value) {
|
||||
@@ -31,6 +36,14 @@ void SendFilesWay::setGroupFiles(bool value) {
|
||||
}
|
||||
}
|
||||
|
||||
void SendFilesWay::setSendLargePhotos(bool value) {
|
||||
if (value) {
|
||||
_flags |= Flag::SendLargePhotos;
|
||||
} else {
|
||||
_flags &= ~Flag::SendLargePhotos;
|
||||
}
|
||||
}
|
||||
|
||||
void SendFilesWay::setHasCompressedStickers(bool value) {
|
||||
if (value) {
|
||||
_flags |= Flag::HasCompressedStickers;
|
||||
@@ -53,16 +66,22 @@ int32 SendFilesWay::serialize() const {
|
||||
: groupFiles()
|
||||
? int32(3)
|
||||
: int32(2);
|
||||
if (sendLargePhotos()) {
|
||||
result |= kSerializedSendLargePhotos;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::optional<SendFilesWay> SendFilesWay::FromSerialized(int32 value) {
|
||||
if (value < 0 || value > 3) {
|
||||
if (value < 0 || value > 7) {
|
||||
return std::nullopt;
|
||||
}
|
||||
const auto sendLargePhotos = (value & kSerializedSendLargePhotos) != 0;
|
||||
value &= ~kSerializedSendLargePhotos;
|
||||
auto result = SendFilesWay();
|
||||
result.setGroupFiles((value == 0) || (value == 3));
|
||||
result.setSendImagesAsPhotos((value == 0) || (value == 1));
|
||||
result.setSendLargePhotos(sendLargePhotos);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -32,8 +32,12 @@ public:
|
||||
[[nodiscard]] bool sendImagesAsPhotos() const {
|
||||
return (_flags & Flag::SendImagesAsPhotos) != 0;
|
||||
}
|
||||
[[nodiscard]] bool sendLargePhotos() const {
|
||||
return (_flags & Flag::SendLargePhotos) != 0;
|
||||
}
|
||||
void setGroupFiles(bool value);
|
||||
void setSendImagesAsPhotos(bool value);
|
||||
void setSendLargePhotos(bool value);
|
||||
void setHasCompressedStickers(bool value);
|
||||
|
||||
[[nodiscard]] inline bool operator<(const SendFilesWay &other) const {
|
||||
@@ -68,6 +72,7 @@ private:
|
||||
GroupFiles = (1 << 0),
|
||||
SendImagesAsPhotos = (1 << 1),
|
||||
HasCompressedStickers = (1 << 2),
|
||||
SendLargePhotos = (1 << 3),
|
||||
|
||||
Default = GroupFiles | SendImagesAsPhotos,
|
||||
};
|
||||
|
||||
@@ -45,7 +45,7 @@ SingleMediaPreview *SingleMediaPreview::Create(
|
||||
&& !hasModifications) {
|
||||
return nullptr;
|
||||
}
|
||||
return CreateChild<SingleMediaPreview>(
|
||||
const auto result = CreateChild<SingleMediaPreview>(
|
||||
parent,
|
||||
st,
|
||||
std::move(gifPaused),
|
||||
@@ -56,6 +56,8 @@ SingleMediaPreview *SingleMediaPreview::Create(
|
||||
animationPreview ? file.path : QString(),
|
||||
type,
|
||||
std::move(actionAllowed));
|
||||
result->setCanShowHighQualityBadge(file.canUseHighQualityPhoto());
|
||||
return result;
|
||||
}
|
||||
|
||||
SingleMediaPreview::SingleMediaPreview(
|
||||
|
||||
@@ -107,6 +107,8 @@ menuIconTranslate: icon {{ "menu/translate", menuIconColor }};
|
||||
menuIconReportAntiSpam: icon {{ "menu/false_positive", menuIconColor }};
|
||||
menuIconSpoiler: icon {{ "menu/spoiler_on", menuIconColor }};
|
||||
menuIconSpoilerOff: icon {{ "menu/spoiler_off", menuIconColor }};
|
||||
menuIconQualityHigh: icon {{ "menu/quality_hd", menuIconColor }};
|
||||
menuIconQualityStandard: icon {{ "menu/quality_sd", menuIconColor }};
|
||||
menuIconDisable: icon {{ "menu/disable", menuIconColor }};
|
||||
menuIconPhotoSet: icon {{ "menu/photo_set", menuIconColor }};
|
||||
menuIconPhotoSuggest: icon {{ "menu/photo_suggest", menuIconColor }};
|
||||
|
||||
Reference in New Issue
Block a user