init
Some checks failed
Docker. / Ubuntu (push) Has been cancelled
User-agent updater. / User-agent (push) Failing after 15s
Lock Threads / lock (push) Failing after 10s
Waiting for answer. / waiting-for-answer (push) Failing after 22s
Needs user action. / needs-user-action (push) Failing after 8s
Can't reproduce. / cant-reproduce (push) Failing after 8s
Close stale issues and PRs / stale (push) Has been cancelled
Some checks failed
Docker. / Ubuntu (push) Has been cancelled
User-agent updater. / User-agent (push) Failing after 15s
Lock Threads / lock (push) Failing after 10s
Waiting for answer. / waiting-for-answer (push) Failing after 22s
Needs user action. / needs-user-action (push) Failing after 8s
Can't reproduce. / cant-reproduce (push) Failing after 8s
Close stale issues and PRs / stale (push) Has been cancelled
This commit is contained in:
423
Telegram/SourceFiles/history/history_drag_area.cpp
Normal file
423
Telegram/SourceFiles/history/history_drag_area.cpp
Normal file
@@ -0,0 +1,423 @@
|
||||
/*
|
||||
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::on_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::on_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();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user