Allow sliding toasts from the parent side.
This commit is contained in:
parent
48a8315740
commit
7849f0561b
7 changed files with 130 additions and 57 deletions
|
|
@ -23,45 +23,50 @@ Instance::Instance(
|
|||
not_null<QWidget*> widgetParent,
|
||||
const Private &)
|
||||
: _st(config.st)
|
||||
, _hideAtMs(crl::now() + config.durationMs) {
|
||||
_widget = std::make_unique<internal::Widget>(widgetParent, config);
|
||||
_a_opacity.start(
|
||||
[=] { opacityAnimationCallback(); },
|
||||
, _hideAt(crl::now() + config.durationMs)
|
||||
, _sliding(config.slideSide != RectPart::None)
|
||||
, _widget(std::make_unique<internal::Widget>(widgetParent, config)) {
|
||||
_shownAnimation.start(
|
||||
[=] { shownAnimationCallback(); },
|
||||
0.,
|
||||
1.,
|
||||
_st->durationFadeIn);
|
||||
_sliding ? _st->durationSlide : _st->durationFadeIn);
|
||||
}
|
||||
|
||||
void SetDefaultParent(not_null<QWidget*> parent) {
|
||||
DefaultParent = parent;
|
||||
}
|
||||
|
||||
void Show(not_null<QWidget*> parent, const Config &config) {
|
||||
base::weak_ptr<Instance> Show(
|
||||
not_null<QWidget*> parent,
|
||||
const Config &config) {
|
||||
const auto manager = internal::Manager::instance(parent);
|
||||
manager->addToast(std::make_unique<Instance>(
|
||||
return manager->addToast(std::make_unique<Instance>(
|
||||
config,
|
||||
parent,
|
||||
Instance::Private()));
|
||||
}
|
||||
|
||||
void Show(const Config &config) {
|
||||
base::weak_ptr<Instance> Show(const Config &config) {
|
||||
if (const auto parent = DefaultParent.data()) {
|
||||
Show(parent, config);
|
||||
return Show(parent, config);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void Show(not_null<QWidget*> parent, const QString &text) {
|
||||
Show(parent, Config{ .text = { text }, .st = &st::defaultToast });
|
||||
base::weak_ptr<Instance> Show(
|
||||
not_null<QWidget*> parent,
|
||||
const QString &text) {
|
||||
return Show(parent, Config{ .text = { text }, .st = &st::defaultToast });
|
||||
}
|
||||
|
||||
void Show(const QString &text) {
|
||||
Show(Config{ .text = { text }, .st = &st::defaultToast });
|
||||
base::weak_ptr<Instance> Show(const QString &text) {
|
||||
return Show(Config{ .text = { text }, .st = &st::defaultToast });
|
||||
}
|
||||
|
||||
void Instance::opacityAnimationCallback() {
|
||||
_widget->setShownLevel(_a_opacity.value(_hiding ? 0. : 1.));
|
||||
_widget->update();
|
||||
if (!_a_opacity.animating()) {
|
||||
void Instance::shownAnimationCallback() {
|
||||
_widget->setShownLevel(_shownAnimation.value(_hiding ? 0. : 1.));
|
||||
if (!_shownAnimation.animating()) {
|
||||
if (_hiding) {
|
||||
hide();
|
||||
}
|
||||
|
|
@ -70,11 +75,11 @@ void Instance::opacityAnimationCallback() {
|
|||
|
||||
void Instance::hideAnimated() {
|
||||
_hiding = true;
|
||||
_a_opacity.start(
|
||||
[=] { opacityAnimationCallback(); },
|
||||
_shownAnimation.start(
|
||||
[=] { shownAnimationCallback(); },
|
||||
1.,
|
||||
0.,
|
||||
_st->durationFadeOut);
|
||||
_sliding ? _st->durationSlide : _st->durationFadeOut);
|
||||
}
|
||||
|
||||
void Instance::hide() {
|
||||
|
|
@ -82,5 +87,9 @@ void Instance::hide() {
|
|||
_widget->deleteLater();
|
||||
}
|
||||
|
||||
not_null<RpWidget*> Instance::widget() const {
|
||||
return _widget.get();
|
||||
}
|
||||
|
||||
} // namespace Toast
|
||||
} // namespace Ui
|
||||
|
|
|
|||
|
|
@ -9,6 +9,9 @@
|
|||
#include "ui/effects/animations.h"
|
||||
#include "ui/text/text_entity.h"
|
||||
#include "ui/click_handler.h"
|
||||
#include "ui/rect_part.h"
|
||||
#include "ui/rp_widget.h"
|
||||
#include "base/weak_ptr.h"
|
||||
|
||||
namespace style {
|
||||
struct Toast;
|
||||
|
|
@ -32,15 +35,13 @@ struct Config {
|
|||
int maxLines = 16;
|
||||
bool multiline = false;
|
||||
bool dark = false;
|
||||
RectPart slideSide = RectPart::None;
|
||||
ClickHandlerFilter filter;
|
||||
};
|
||||
void SetDefaultParent(not_null<QWidget*> parent);
|
||||
void Show(not_null<QWidget*> parent, const Config &config);
|
||||
void Show(const Config &config);
|
||||
void Show(not_null<QWidget*> parent, const QString &text);
|
||||
void Show(const QString &text);
|
||||
|
||||
class Instance {
|
||||
void SetDefaultParent(not_null<QWidget*> parent);
|
||||
|
||||
class Instance final : public base::has_weak_ptr {
|
||||
struct Private {
|
||||
};
|
||||
|
||||
|
|
@ -55,22 +56,35 @@ public:
|
|||
void hideAnimated();
|
||||
void hide();
|
||||
|
||||
[[nodiscard]] not_null<RpWidget*> widget() const;
|
||||
|
||||
private:
|
||||
void opacityAnimationCallback();
|
||||
void shownAnimationCallback();
|
||||
|
||||
const not_null<const style::Toast*> _st;
|
||||
const crl::time _hideAt = 0;
|
||||
|
||||
Ui::Animations::Simple _shownAnimation;
|
||||
bool _hiding = false;
|
||||
Ui::Animations::Simple _a_opacity;
|
||||
|
||||
const crl::time _hideAtMs;
|
||||
bool _sliding = false;
|
||||
|
||||
// ToastManager should reset _widget pointer if _widget is destroyed.
|
||||
friend class internal::Manager;
|
||||
friend void Show(not_null<QWidget*> parent, const Config &config);
|
||||
friend base::weak_ptr<Instance> Show(
|
||||
not_null<QWidget*> parent,
|
||||
const Config &config);
|
||||
std::unique_ptr<internal::Widget> _widget;
|
||||
|
||||
};
|
||||
|
||||
base::weak_ptr<Instance> Show(
|
||||
not_null<QWidget*> parent,
|
||||
const Config &config);
|
||||
base::weak_ptr<Instance> Show(const Config &config);
|
||||
base::weak_ptr<Instance> Show(
|
||||
not_null<QWidget*> parent,
|
||||
const QString &text);
|
||||
base::weak_ptr<Instance> Show(const QString &text);
|
||||
|
||||
} // namespace Toast
|
||||
} // namespace Ui
|
||||
|
|
|
|||
|
|
@ -44,13 +44,16 @@ not_null<Manager*> Manager::instance(not_null<QWidget*> parent) {
|
|||
return i->second;
|
||||
}
|
||||
|
||||
void Manager::addToast(std::unique_ptr<Instance> &&toast) {
|
||||
base::weak_ptr<Instance> Manager::addToast(
|
||||
std::unique_ptr<Instance> &&toast) {
|
||||
_toasts.push_back(std::move(toast));
|
||||
const auto t = _toasts.back().get();
|
||||
const auto widget = t->_widget.get();
|
||||
|
||||
_toastByWidget.emplace(widget, t);
|
||||
connect(widget, SIGNAL(destroyed(QObject*)), this, SLOT(onToastWidgetDestroyed(QObject*)));
|
||||
connect(widget, &QObject::destroyed, [=] {
|
||||
toastWidgetDestroyed(widget);
|
||||
});
|
||||
if (const auto parent = widget->parentWidget()) {
|
||||
auto found = false;
|
||||
for (auto i = _toastParents.begin(); i != _toastParents.cend();) {
|
||||
|
|
@ -72,10 +75,11 @@ void Manager::addToast(std::unique_ptr<Instance> &&toast) {
|
|||
const auto nearestHide = _toastByHideTime.empty()
|
||||
? 0LL
|
||||
: _toastByHideTime.begin()->first;
|
||||
_toastByHideTime.emplace(t->_hideAtMs, t);
|
||||
_toastByHideTime.emplace(t->_hideAt, t);
|
||||
if (!nearestHide || _toastByHideTime.begin()->first < nearestHide) {
|
||||
startNextHideTimer();
|
||||
}
|
||||
return make_weak(t);
|
||||
}
|
||||
|
||||
void Manager::hideByTimer() {
|
||||
|
|
@ -92,7 +96,7 @@ void Manager::hideByTimer() {
|
|||
startNextHideTimer();
|
||||
}
|
||||
|
||||
void Manager::onToastWidgetDestroyed(QObject *widget) {
|
||||
void Manager::toastWidgetDestroyed(QObject *widget) {
|
||||
const auto i = _toastByWidget.find(static_cast<Widget*>(widget));
|
||||
if (i == _toastByWidget.cend()) {
|
||||
return;
|
||||
|
|
@ -101,6 +105,13 @@ void Manager::onToastWidgetDestroyed(QObject *widget) {
|
|||
_toastByWidget.erase(i);
|
||||
toast->_widget.release();
|
||||
|
||||
for (auto i = begin(_toastByHideTime); i != end(_toastByHideTime); ++i) {
|
||||
if (i->second == toast) {
|
||||
_toastByHideTime.erase(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const auto j = ranges::find(
|
||||
_toasts,
|
||||
toast.get(),
|
||||
|
|
@ -111,7 +122,9 @@ void Manager::onToastWidgetDestroyed(QObject *widget) {
|
|||
}
|
||||
|
||||
void Manager::startNextHideTimer() {
|
||||
if (_toastByHideTime.empty()) return;
|
||||
if (_toastByHideTime.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto ms = crl::now();
|
||||
if (ms >= _toastByHideTime.begin()->first) {
|
||||
|
|
|
|||
|
|
@ -14,9 +14,7 @@ namespace Toast {
|
|||
namespace internal {
|
||||
|
||||
class Widget;
|
||||
class Manager : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
class Manager final : public QObject {
|
||||
struct CreateTag {
|
||||
};
|
||||
|
||||
|
|
@ -28,15 +26,13 @@ public:
|
|||
|
||||
static not_null<Manager*> instance(not_null<QWidget*> parent);
|
||||
|
||||
void addToast(std::unique_ptr<Instance> &&toast);
|
||||
base::weak_ptr<Instance> addToast(std::unique_ptr<Instance> &&toast);
|
||||
|
||||
protected:
|
||||
bool eventFilter(QObject *o, QEvent *e);
|
||||
|
||||
private slots:
|
||||
void onToastWidgetDestroyed(QObject *widget);
|
||||
|
||||
private:
|
||||
void toastWidgetDestroyed(QObject *widget);
|
||||
void startNextHideTimer();
|
||||
void hideByTimer();
|
||||
|
||||
|
|
|
|||
|
|
@ -17,9 +17,10 @@ namespace Toast {
|
|||
namespace internal {
|
||||
|
||||
Widget::Widget(QWidget *parent, const Config &config)
|
||||
: TWidget(parent)
|
||||
: RpWidget(parent)
|
||||
, _st(config.st)
|
||||
, _roundRect(ImageRoundRadius::Large, st::toastBg)
|
||||
, _slideSide(config.slideSide)
|
||||
, _multiline(config.multiline)
|
||||
, _dark(config.dark)
|
||||
, _maxTextWidth(widthWithoutPadding(_st->maxWidth))
|
||||
|
|
@ -49,36 +50,74 @@ Widget::Widget(QWidget *parent, const Config &config)
|
|||
}
|
||||
|
||||
void Widget::onParentResized() {
|
||||
auto newWidth = _st->maxWidth;
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
void Widget::updateGeometry() {
|
||||
auto width = _st->maxWidth;
|
||||
accumulate_min(
|
||||
newWidth,
|
||||
width,
|
||||
_st->padding.left() + _text.maxWidth() + _st->padding.right());
|
||||
accumulate_min(
|
||||
newWidth,
|
||||
width,
|
||||
parentWidget()->width() - _st->margin.left() - _st->margin.right());
|
||||
_textWidth = widthWithoutPadding(newWidth);
|
||||
_textWidth = widthWithoutPadding(width);
|
||||
const auto textHeight = _multiline
|
||||
? qMin(_text.countHeight(_textWidth), _maxTextHeight)
|
||||
: _text.minHeight();
|
||||
const auto newHeight = _st->padding.top()
|
||||
const auto height = _st->padding.top()
|
||||
+ textHeight
|
||||
+ _st->padding.bottom();
|
||||
setGeometry(
|
||||
(parentWidget()->width() - newWidth) / 2,
|
||||
(parentWidget()->height() - newHeight) / 2,
|
||||
newWidth,
|
||||
newHeight);
|
||||
const auto rect = QRect(0, 0, width, height);
|
||||
const auto outer = parentWidget()->size();
|
||||
const auto full = QPoint(outer.width(), outer.height());
|
||||
const auto middle = QPoint(
|
||||
(outer.width() - width) / 2,
|
||||
(outer.height() - height) / 2);
|
||||
const auto interpolated = [&](int from, int to) {
|
||||
return anim::interpolate(from, to, _shownLevel);
|
||||
};
|
||||
setGeometry(rect.translated([&] {
|
||||
switch (_slideSide) {
|
||||
case RectPart::None:
|
||||
return middle;
|
||||
case RectPart::Left:
|
||||
return QPoint(
|
||||
interpolated(-width, _st->margin.left()),
|
||||
middle.y());
|
||||
case RectPart::Top:
|
||||
return QPoint(
|
||||
middle.x(),
|
||||
interpolated(-height, _st->margin.top()));
|
||||
case RectPart::Right:
|
||||
return QPoint(
|
||||
full.x() - interpolated(0, width + _st->margin.right()),
|
||||
middle.y());
|
||||
case RectPart::Bottom:
|
||||
return QPoint(
|
||||
middle.x(),
|
||||
full.y() - interpolated(0, height + _st->margin.bottom()));
|
||||
}
|
||||
Unexpected("Slide side in Toast::Widget::updateGeometry.");
|
||||
}()));
|
||||
}
|
||||
|
||||
void Widget::setShownLevel(float64 shownLevel) {
|
||||
_shownLevel = shownLevel;
|
||||
if (_slideSide != RectPart::None) {
|
||||
updateGeometry();
|
||||
} else {
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
void Widget::paintEvent(QPaintEvent *e) {
|
||||
Painter p(this);
|
||||
PainterHighQualityEnabler hq(p);
|
||||
|
||||
if (_slideSide == RectPart::None) {
|
||||
p.setOpacity(_shownLevel);
|
||||
}
|
||||
_roundRect.paint(p, rect());
|
||||
if (_dark) {
|
||||
_roundRect.paint(p, rect());
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ namespace Ui {
|
|||
namespace Toast {
|
||||
namespace internal {
|
||||
|
||||
class Widget : public TWidget {
|
||||
class Widget final : public RpWidget {
|
||||
public:
|
||||
Widget(QWidget *parent, const Config &config);
|
||||
|
||||
|
|
@ -34,9 +34,11 @@ protected:
|
|||
|
||||
private:
|
||||
[[nodiscard]] int widthWithoutPadding(int w) const;
|
||||
void updateGeometry();
|
||||
|
||||
const not_null<const style::Toast*> _st;
|
||||
RoundRect _roundRect;
|
||||
RectPart _slideSide = RectPart::None;
|
||||
|
||||
float64 _shownLevel = 0;
|
||||
bool _multiline = false;
|
||||
|
|
|
|||
|
|
@ -1289,7 +1289,7 @@ defaultToast: Toast {
|
|||
maxWidth: 480px;
|
||||
durationFadeIn: 200;
|
||||
durationFadeOut: 1000;
|
||||
durationSlide: 400;
|
||||
durationSlide: 160;
|
||||
}
|
||||
|
||||
// Windows specific title
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue