Add files from clipboard to composed album.

Fixes #4243.
This commit is contained in:
John Preston 2017-12-29 20:02:23 +03:00
parent 0171a4e874
commit 2a409e3734
8 changed files with 189 additions and 30 deletions

View File

@ -1329,8 +1329,7 @@ SendFilesBox::SendFilesBox(
Storage::PreparedList &&list,
CompressConfirm compressed)
: _list(std::move(list))
, _compressConfirm(compressed)
, _caption(this, st::confirmCaptionArea, FieldPlaceholder(_list)) {
, _compressConfirm(compressed) {
}
void SendFilesBox::initPreview(rpl::producer<int> desiredPreviewHeight) {
@ -1398,7 +1397,7 @@ void SendFilesBox::setupShadows(
const auto topShadow = Ui::CreateChild<Ui::FadeShadow>(this);
const auto bottomShadow = Ui::CreateChild<Ui::FadeShadow>(this);
wrap->geometryValue(
) | rpl::start_with_next([=](const QRect &geometry) {
) | rpl::start_with_next_done([=](const QRect &geometry) {
topShadow->resizeToWidth(geometry.width());
topShadow->move(
geometry.x(),
@ -1407,7 +1406,11 @@ void SendFilesBox::setupShadows(
bottomShadow->move(
geometry.x(),
geometry.y() + geometry.height() - st::lineWidth);
}, [t = make_weak(topShadow), b = make_weak(bottomShadow)] {
Ui::DestroyChild(t.data());
Ui::DestroyChild(b.data());
}, topShadow->lifetime());
topShadow->toggleOn(wrap->scrollTopValue() | rpl::map(_1 > 0));
bottomShadow->toggleOn(rpl::combine(
wrap->scrollTopValue(),
@ -1422,17 +1425,7 @@ void SendFilesBox::prepare() {
_send = addButton(langFactory(lng_send_button), [this] { send(); });
addButton(langFactory(lng_cancel), [this] { closeBox(); });
initSendWay();
if (_list.files.size() == 1) {
prepareSingleFilePreview();
} else {
if (_list.albumIsPossible) {
prepareAlbumPreview();
} else {
auto desiredPreviewHeight = rpl::single(0);
initPreview(std::move(desiredPreviewHeight));
}
}
preparePreview();
subscribe(boxClosing, [this] {
if (!_confirmed && _cancelledCallback) {
_cancelledCallback();
@ -1473,6 +1466,26 @@ void SendFilesBox::initSendWay() {
: SendFilesWay::Photos;
}();
_sendWay = std::make_shared<Ui::RadioenumGroup<SendFilesWay>>(value);
_sendWay->setChangedCallback([this](SendFilesWay value) {
applyAlbumOrder();
if (_albumPreview) {
_albumPreview->setSendWay(value);
}
setInnerFocus();
});
}
void SendFilesBox::preparePreview() {
if (_list.files.size() == 1) {
prepareSingleFilePreview();
} else {
if (_list.albumIsPossible) {
prepareAlbumPreview();
} else {
auto desiredPreviewHeight = rpl::single(0);
initPreview(std::move(desiredPreviewHeight));
}
}
}
void SendFilesBox::setupControls() {
@ -1482,15 +1495,19 @@ void SendFilesBox::setupControls() {
}
void SendFilesBox::setupSendWayControls() {
_sendAlbum.destroy();
_sendPhotos.destroy();
_sendFiles.destroy();
if (_compressConfirm == CompressConfirm::None) {
return;
}
const auto addRadio = [&](
object_ptr<Ui::Radioenum<SendFilesWay>> &button,
SendFilesWay value,
const QString &text) {
object_ptr<Ui::Radioenum<SendFilesWay>> &button,
SendFilesWay value,
const QString &text) {
const auto &style = st::defaultBoxCheckbox;
button.create(this, _sendWay, value, text, style);
button->show();
};
if (_list.albumIsPossible) {
addRadio(_sendAlbum, SendFilesWay::Album, lang(lng_send_album));
@ -1507,17 +1524,12 @@ void SendFilesBox::setupSendWayControls() {
addRadio(_sendFiles, SendFilesWay::Files, (_list.files.size() == 1)
? lang(lng_send_file)
: lng_send_files(lt_count, _list.files.size()));
_sendWay->setChangedCallback([this](SendFilesWay value) {
if (_albumPreview) {
applyAlbumOrder();
_albumPreview->setSendWay(value);
}
setInnerFocus();
});
}
void SendFilesBox::applyAlbumOrder() {
Expects(_albumPreview != nullptr);
if (!_albumPreview) {
return;
}
const auto order = _albumPreview->takeOrder();
const auto isDefault = [&] {
@ -1536,10 +1548,12 @@ void SendFilesBox::applyAlbumOrder() {
}
void SendFilesBox::setupCaption() {
if (!_caption) {
if (_caption) {
_caption->setPlaceholder(FieldPlaceholder(_list));
return;
}
_caption.create(this, st::confirmCaptionArea, FieldPlaceholder(_list));
_caption->setMaxLength(MaxPhotoCaption);
_caption->setCtrlEnterSubmit(Ui::CtrlEnterSubmit::Both);
connect(_caption, &Ui::InputArea::resized, this, [this] {
@ -1552,6 +1566,16 @@ void SendFilesBox::setupCaption() {
connect(_caption, &Ui::InputArea::cancelled, this, [this] {
closeBox();
});
_caption->setMimeDataHook([this](
not_null<const QMimeData*> data,
Ui::InputArea::MimeAction action) {
if (action == Ui::InputArea::MimeAction::Check) {
return canAddFiles(data);
} else if (action == Ui::InputArea::MimeAction::Insert) {
return addFiles(data);
}
Unexpected("action in MimeData hook.");
});
}
void SendFilesBox::captionResized() {
@ -1560,6 +1584,73 @@ void SendFilesBox::captionResized() {
update();
}
bool SendFilesBox::canAddFiles(not_null<const QMimeData*> data) const {
auto files = 0;
if (data->hasUrls()) {
for (const auto &url : data->urls()) {
if (url.isLocalFile()) {
++files;
}
}
} else if (data->hasImage()) {
++files;
}
if (_list.files.size() + files > Storage::MaxAlbumItems()) {
return false;
} else if (_list.files.size() > 1 && !_albumPreview) {
return false;
} else if (_list.files.front().type
== Storage::PreparedFile::AlbumType::None) {
return false;
}
return true;
}
bool SendFilesBox::addFiles(not_null<const QMimeData*> data) {
auto list = [&] {
if (data->hasUrls()) {
return Storage::PrepareMediaList(
data->urls(),
st::sendMediaPreviewSize);
} else if (data->hasImage()) {
auto image = qvariant_cast<QImage>(data->imageData());
if (!image.isNull()) {
return Storage::PrepareMediaFromImage(
std::move(image),
QByteArray(),
st::sendMediaPreviewSize);
}
}
return Storage::PreparedList(
Storage::PreparedList::Error::EmptyFile,
QString());
}();
if (_list.files.size() + list.files.size() > Storage::MaxAlbumItems()) {
return false;
} else if (list.error != Storage::PreparedList::Error::None) {
return false;
} else if (list.files.size() != 1 && !list.albumIsPossible) {
return false;
} else if (list.files.front().type
== Storage::PreparedFile::AlbumType::None) {
return false;
} else if (_list.files.size() > 1 && !_albumPreview) {
return false;
}
applyAlbumOrder();
delete base::take(_preview);
_albumPreview = nullptr;
if (_list.files.size() == 1
&& _sendWay->value() == SendFilesWay::Photos) {
_sendWay->setValue(SendFilesWay::Album);
}
_list.mergeToEnd(std::move(list));
preparePreview();
updateControlsGeometry();
return true;
}
void SendFilesBox::setupTitleText() {
if (_list.files.size() > 1) {
const auto onlyImages = (_compressConfirm != CompressConfirm::None)
@ -1681,9 +1772,7 @@ void SendFilesBox::send(bool ctrlShiftEnter) {
}
}
if (_albumPreview) {
applyAlbumOrder();
}
applyAlbumOrder();
_confirmed = true;
if (_confirmedCallback) {
auto caption = _caption

View File

@ -83,6 +83,7 @@ private:
not_null<Ui::ScrollArea*> wrap,
not_null<AlbumPreview*> content);
void preparePreview();
void prepareSingleFilePreview();
void prepareAlbumPreview();
void applyAlbumOrder();
@ -94,6 +95,9 @@ private:
void updateBoxSize();
void updateControlsGeometry();
bool canAddFiles(not_null<const QMimeData*> data) const;
bool addFiles(not_null<const QMimeData*> data);
QString _titleText;
int _titleHeight = 0;

View File

@ -104,7 +104,6 @@ private:
std::vector<not_null<Thumb*>> _dying;
base::flat_map<Key, std::unique_ptr<Thumb>> _cache;
int _width = 0;
int _limit = 0;
QRect _updatedRect;
rpl::event_stream<QRect> _updateRequests;

View File

@ -287,5 +287,34 @@ PreparedList PreparedList::Reordered(
return result;
}
void PreparedList::mergeToEnd(PreparedList &&other) {
if (error != Error::None) {
return;
}
if (other.error != Error::None) {
error = other.error;
errorData = other.errorData;
return;
}
allFilesForCompress = allFilesForCompress && other.allFilesForCompress;
files.reserve(files.size() + other.files.size());
for (auto &file : other.files) {
files.push_back(std::move(file));
}
if (files.size() > 1 && files.size() <= kMaxAlbumCount) {
const auto badIt = ranges::find(
files,
PreparedFile::AlbumType::None,
[](const PreparedFile &file) { return file.type; });
albumIsPossible = (badIt == files.end());
} else {
albumIsPossible = false;
}
}
int MaxAlbumItems() {
return kMaxAlbumCount;
}
} // namespace Storage

View File

@ -71,6 +71,7 @@ struct PreparedList {
static PreparedList Reordered(
PreparedList &&list,
std::vector<int> order);
void mergeToEnd(PreparedList &&other);
Error error = Error::None;
QString errorData;
@ -87,5 +88,6 @@ PreparedList PrepareMediaFromImage(
QImage &&image,
QByteArray &&content,
int previewWidth);
int MaxAlbumItems();
} // namespace Storage

View File

@ -59,6 +59,10 @@ inline Widget *CreateChild(
return new Widget(parent, std::forward<Args>(args)...);
}
inline void DestroyChild(QWidget *child) {
delete child;
}
template <typename Value>
inline void AttachAsChild(not_null<QObject*> parent, Value &&value) {
using PlainValue = std::decay_t<Value>;

View File

@ -2467,6 +2467,24 @@ void InputArea::Inner::contextMenuEvent(QContextMenuEvent *e) {
}
}
bool InputArea::Inner::canInsertFromMimeData(const QMimeData *source) const {
if (source
&& f()->_mimeDataHook
&& f()->_mimeDataHook(source, MimeAction::Check)) {
return true;
}
return QTextEdit::canInsertFromMimeData(source);
}
void InputArea::Inner::insertFromMimeData(const QMimeData *source) {
if (source
&& f()->_mimeDataHook
&& f()->_mimeDataHook(source, MimeAction::Insert)) {
return;
}
return QTextEdit::insertFromMimeData(source);
}
void InputArea::resizeEvent(QResizeEvent *e) {
refreshPlaceholder();
_inner->setGeometry(rect().marginsRemoved(_st.textMargins));

View File

@ -387,6 +387,17 @@ public:
_inner->clearFocus();
}
enum class MimeAction {
Check,
Insert,
};
using MimeDataHook = base::lambda<bool(
not_null<const QMimeData*> data,
MimeAction action)>;
void setMimeDataHook(MimeDataHook hook) {
_mimeDataHook = std::move(hook);
}
private slots:
void onTouchTimer();
@ -441,6 +452,8 @@ private:
void keyPressEvent(QKeyEvent *e) override;
void contextMenuEvent(QContextMenuEvent *e) override;
bool canInsertFromMimeData(const QMimeData *source) const override;
void insertFromMimeData(const QMimeData *source) override;
QMimeData *createMimeDataFromSelection() const override;
private:
@ -504,6 +517,7 @@ private:
QPoint _touchStart;
bool _correcting = false;
MimeDataHook _mimeDataHook;
};