| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423 |
- /*
- 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 "history/history_drag_area.h"
- #include "base/event_filter.h"
- #include "ui/boxes/confirm_box.h"
- #include "boxes/sticker_set_box.h"
- #include "inline_bots/inline_bot_result.h"
- #include "inline_bots/inline_bot_layout_item.h"
- #include "dialogs/ui/dialogs_layout.h"
- #include "history/history_widget.h"
- #include "storage/localstorage.h"
- #include "lang/lang_keys.h"
- #include "ui/widgets/shadow.h"
- #include "ui/painter.h"
- #include "ui/ui_utility.h"
- #include "ui/cached_round_corners.h"
- #include "mainwindow.h"
- #include "apiwrap.h"
- #include "mainwidget.h"
- #include "storage/storage_media_prepare.h"
- #include "styles/style_chat_helpers.h"
- #include "styles/style_layers.h"
- namespace {
- constexpr auto kDragAreaEvents = {
- QEvent::DragEnter,
- QEvent::DragLeave,
- QEvent::Drop,
- QEvent::MouseButtonRelease,
- QEvent::Leave,
- };
- } // namespace
- DragArea::Areas DragArea::SetupDragAreaToContainer(
- not_null<Ui::RpWidget*> container,
- Fn<bool(not_null<const QMimeData*>)> &&dragEnterFilter,
- Fn<void(bool)> &&setAcceptDropsField,
- Fn<void()> &&updateControlsGeometry,
- DragArea::CallbackComputeState &&computeState,
- bool hideSubtext) {
- using DragState = Storage::MimeDataState;
- auto &lifetime = container->lifetime();
- container->setAcceptDrops(true);
- const auto attachDragDocument
- = Ui::CreateChild<DragArea>(container.get());
- const auto attachDragPhoto = Ui::CreateChild<DragArea>(container.get());
- attachDragDocument->hide();
- attachDragPhoto->hide();
- attachDragDocument->raise();
- attachDragPhoto->raise();
- const auto attachDragState
- = lifetime.make_state<DragState>(DragState::None);
- const auto width = [=] {
- return container->width();
- };
- const auto height = [=] {
- return container->height();
- };
- const auto horizontalMargins = st::dragMargin.left()
- + st::dragMargin.right();
- const auto verticalMargins = st::dragMargin.top()
- + st::dragMargin.bottom();
- const auto resizeToFull = [=](not_null<DragArea*> w) {
- w->resize(width() - horizontalMargins, height() - verticalMargins);
- };
- const auto moveToTop = [=](not_null<DragArea*> w) {
- w->move(st::dragMargin.left(), st::dragMargin.top());
- };
- const auto updateAttachGeometry = crl::guard(container, [=] {
- if (updateControlsGeometry) {
- updateControlsGeometry();
- }
- switch (*attachDragState) {
- case DragState::Files:
- resizeToFull(attachDragDocument);
- moveToTop(attachDragDocument);
- break;
- case DragState::PhotoFiles:
- attachDragDocument->resize(
- width() - horizontalMargins,
- (height() - verticalMargins) / 2);
- moveToTop(attachDragDocument);
- attachDragPhoto->resize(
- attachDragDocument->width(),
- attachDragDocument->height());
- attachDragPhoto->move(
- st::dragMargin.left(),
- height()
- - attachDragPhoto->height()
- - st::dragMargin.bottom());
- break;
- case DragState::Image:
- resizeToFull(attachDragPhoto);
- moveToTop(attachDragPhoto);
- break;
- }
- });
- const auto updateDragAreas = [=] {
- if (setAcceptDropsField) {
- setAcceptDropsField(*attachDragState == DragState::None);
- }
- updateAttachGeometry();
- switch (*attachDragState) {
- case DragState::None:
- attachDragDocument->otherLeave();
- attachDragPhoto->otherLeave();
- break;
- case DragState::Files:
- attachDragDocument->setText(
- tr::lng_drag_files_here(tr::now),
- hideSubtext
- ? QString()
- : tr::lng_drag_to_send_files(tr::now));
- attachDragDocument->otherEnter();
- attachDragPhoto->hideFast();
- break;
- case DragState::PhotoFiles:
- attachDragDocument->setText(
- tr::lng_drag_images_here(tr::now),
- hideSubtext
- ? QString()
- : tr::lng_drag_to_send_no_compression(tr::now));
- attachDragPhoto->setText(
- tr::lng_drag_photos_here(tr::now),
- hideSubtext
- ? QString()
- : tr::lng_drag_to_send_quick(tr::now));
- attachDragDocument->otherEnter();
- attachDragPhoto->otherEnter();
- break;
- case DragState::Image:
- attachDragPhoto->setText(
- tr::lng_drag_images_here(tr::now),
- hideSubtext
- ? QString()
- : tr::lng_drag_to_send_quick(tr::now));
- attachDragDocument->hideFast();
- attachDragPhoto->otherEnter();
- break;
- };
- };
- container->sizeValue(
- ) | rpl::start_with_next(updateAttachGeometry, lifetime);
- const auto resetDragStateIfNeeded = [=] {
- if (*attachDragState != DragState::None
- || !attachDragPhoto->isHidden()
- || !attachDragDocument->isHidden()) {
- *attachDragState = DragState::None;
- updateDragAreas();
- }
- };
- const auto dragEnterEvent = [=](QDragEnterEvent *e) {
- if (dragEnterFilter && !dragEnterFilter(e->mimeData())) {
- return;
- }
- *attachDragState = computeState
- ? computeState(e->mimeData())
- : Storage::ComputeMimeDataState(e->mimeData());
- updateDragAreas();
- if (*attachDragState != DragState::None) {
- e->setDropAction(Qt::IgnoreAction);
- e->accept();
- }
- };
- const auto dragLeaveEvent = [=](QDragLeaveEvent *e) {
- resetDragStateIfNeeded();
- };
- const auto dropEvent = [=](QDropEvent *e) {
- // Hide fast to avoid visual bugs in resizable boxes.
- attachDragDocument->hideFast();
- attachDragPhoto->hideFast();
- *attachDragState = DragState::None;
- updateDragAreas();
- e->setDropAction(Qt::CopyAction);
- e->accept();
- };
- const auto processDragEvents = [=](not_null<QEvent*> event) {
- switch (event->type()) {
- case QEvent::DragEnter:
- dragEnterEvent(static_cast<QDragEnterEvent*>(event.get()));
- return true;
- case QEvent::DragLeave:
- dragLeaveEvent(static_cast<QDragLeaveEvent*>(event.get()));
- return true;
- case QEvent::Drop:
- dropEvent(static_cast<QDropEvent*>(event.get()));
- return true;
- };
- return false;
- };
- container->events(
- ) | rpl::filter([=](not_null<QEvent*> event) {
- return ranges::contains(kDragAreaEvents, event->type());
- }) | rpl::start_with_next([=](not_null<QEvent*> event) {
- const auto type = event->type();
- if (processDragEvents(event)) {
- return;
- } else if (type == QEvent::Leave
- || type == QEvent::MouseButtonRelease) {
- resetDragStateIfNeeded();
- }
- }, lifetime);
- const auto eventFilter = [=](not_null<QEvent*> event) {
- processDragEvents(event);
- return base::EventFilterResult::Continue;
- };
- base::install_event_filter(attachDragDocument, eventFilter);
- base::install_event_filter(attachDragPhoto, eventFilter);
- updateDragAreas();
- return {
- .document = attachDragDocument,
- .photo = attachDragPhoto,
- };
- }
- DragArea::DragArea(QWidget *parent) : Ui::RpWidget(parent) {
- setMouseTracking(true);
- setAcceptDrops(true);
- }
- bool DragArea::overlaps(const QRect &globalRect) {
- if (isHidden() || _a_opacity.animating()) {
- return false;
- }
- const auto inner = rect() - st::dragPadding;
- const auto testRect = QRect(
- mapFromGlobal(globalRect.topLeft()),
- globalRect.size());
- const auto h = QMargins(st::boxRadius, 0, st::boxRadius, 0);
- const auto v = QMargins(0, st::boxRadius, 0, st::boxRadius);
- return inner.marginsRemoved(h).contains(testRect)
- || inner.marginsRemoved(v).contains(testRect);
- }
- void DragArea::mouseMoveEvent(QMouseEvent *e) {
- if (_hiding) {
- return;
- }
- setIn((rect() - st::dragPadding).contains(e->pos()));
- }
- void DragArea::dragMoveEvent(QDragMoveEvent *e) {
- setIn((rect() - st::dragPadding).contains(e->pos()));
- e->setDropAction(_in ? Qt::CopyAction : Qt::IgnoreAction);
- e->accept();
- }
- void DragArea::setIn(bool in) {
- if (_in != in) {
- _in = in;
- _a_in.start(
- [=] { update(); },
- _in ? 0. : 1.,
- _in ? 1. : 0.,
- st::boxDuration);
- }
- }
- void DragArea::setText(const QString &text, const QString &subtext) {
- _text = text;
- _subtext = subtext;
- update();
- }
- void DragArea::paintEvent(QPaintEvent *e) {
- Painter p(this);
- const auto opacity = _a_opacity.value(_hiding ? 0. : 1.);
- if (!_a_opacity.animating() && _hiding) {
- return;
- }
- p.setOpacity(opacity);
- const auto inner = rect() - st::dragPadding;
- if (!_cache.isNull()) {
- p.drawPixmapLeft(
- inner.x() - st::boxRoundShadow.extend.left(),
- inner.y() - st::boxRoundShadow.extend.top(),
- width(),
- _cache);
- return;
- }
- Ui::Shadow::paint(p, inner, width(), st::boxRoundShadow);
- Ui::FillRoundRect(p, inner, st::boxBg, Ui::BoxCorners);
- p.setPen(anim::pen(
- st::dragColor,
- st::dragDropColor,
- _a_in.value(_in ? 1. : 0.)));
- p.setFont(st::dragFont);
- const auto rText = QRect(
- 0,
- (height() - st::dragHeight) / 2,
- width(),
- st::dragFont->height);
- p.drawText(rText, _text, QTextOption(style::al_top));
- p.setFont(st::dragSubfont);
- const auto rSubtext = QRect(
- 0,
- (height() + st::dragHeight) / 2 - st::dragSubfont->height,
- width(),
- st::dragSubfont->height * 2);
- p.drawText(rSubtext, _subtext, QTextOption(style::al_top));
- }
- void DragArea::dragEnterEvent(QDragEnterEvent *e) {
- e->setDropAction(Qt::IgnoreAction);
- e->accept();
- }
- void DragArea::dragLeaveEvent(QDragLeaveEvent *e) {
- setIn(false);
- }
- void DragArea::dropEvent(QDropEvent *e) {
- if (e->isAccepted() && _droppedCallback) {
- _droppedCallback(e->mimeData());
- }
- }
- void DragArea::otherEnter() {
- showStart();
- }
- void DragArea::otherLeave() {
- hideStart();
- }
- void DragArea::hideFast() {
- _a_opacity.stop();
- hide();
- }
- void DragArea::hideStart() {
- if (_hiding || isHidden()) {
- return;
- }
- if (_cache.isNull()) {
- _cache = Ui::GrabWidget(
- this,
- rect() - st::dragPadding + st::boxRoundShadow.extend);
- }
- _hiding = true;
- setIn(false);
- _a_opacity.start(
- [=] { opacityAnimationCallback(); },
- 1.,
- 0.,
- st::boxDuration);
- }
- void DragArea::hideFinish() {
- hide();
- _in = false;
- _a_in.stop();
- }
- void DragArea::showStart() {
- if (!_hiding && !isHidden()) {
- return;
- }
- _hiding = false;
- if (_cache.isNull()) {
- _cache = Ui::GrabWidget(
- this,
- rect() - st::dragPadding + st::boxRoundShadow.extend);
- }
- show();
- _a_opacity.start(
- [=] { opacityAnimationCallback(); },
- 0.,
- 1.,
- st::boxDuration);
- }
- void DragArea::opacityAnimationCallback() {
- update();
- if (!_a_opacity.animating()) {
- _cache = QPixmap();
- if (_hiding) {
- hideFinish();
- }
- }
- }
|