mirror of
https://github.com/kotatogram/kotatogram-desktop
synced 2025-08-31 06:35:14 +00:00
Notifications settings done: screen corner selection + max count.
This commit is contained in:
@@ -98,3 +98,15 @@ notificationsBoxScreenSize: size(280px, 160px);
|
||||
notificationsBoxScreenBg: titleBg;
|
||||
notificationsBoxCountLabelTop: 80px;
|
||||
notificationsBoxCountTop: 30px;
|
||||
|
||||
notificationsSampleSkip: 5px;
|
||||
notificationsSampleTopSkip: 5px;
|
||||
notificationsSampleBottomSkip: 5px;
|
||||
notificationsSampleMargin: 2px;
|
||||
|
||||
notificationSampleOpacity: 0.5;
|
||||
notificationSampleSize: size(64px, 16px);
|
||||
notificationSampleUserpicFg: #40ace3;
|
||||
notificationSampleCloseFg: #d7d7d7;
|
||||
notificationSampleTextFg: #d7d7d7;
|
||||
notificationSampleNameFg: #939393;
|
||||
|
@@ -25,6 +25,10 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
||||
#include "ui/buttons/round_button.h"
|
||||
#include "ui/widgets/discrete_slider.h"
|
||||
#include "styles/style_boxes.h"
|
||||
#include "styles/style_dialogs.h"
|
||||
#include "styles/style_window.h"
|
||||
#include "application.h"
|
||||
#include "localstorage.h"
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -32,21 +36,83 @@ constexpr int kMaxNotificationsCount = 5;
|
||||
|
||||
} // namespace
|
||||
|
||||
class NotificationsBox::SampleWidget : public QWidget {
|
||||
public:
|
||||
SampleWidget(NotificationsBox *owner, const QPixmap &cache) : QWidget(nullptr)
|
||||
, _owner(owner)
|
||||
, _cache(cache) {
|
||||
resize(cache.width() / cache.devicePixelRatio(), cache.height() / cache.devicePixelRatio());
|
||||
|
||||
setAttribute(Qt::WA_MacAlwaysShowToolWindow);
|
||||
setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
setAttribute(Qt::WA_OpaquePaintEvent);
|
||||
setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint | Qt::FramelessWindowHint | Qt::BypassWindowManagerHint | Qt::NoDropShadowWindowHint);
|
||||
|
||||
setWindowOpacity(0.);
|
||||
show();
|
||||
}
|
||||
|
||||
void detach() {
|
||||
_owner = nullptr;
|
||||
hideFast();
|
||||
}
|
||||
|
||||
void showFast() {
|
||||
_hiding = false;
|
||||
startAnimation();
|
||||
}
|
||||
|
||||
void hideFast() {
|
||||
_hiding = true;
|
||||
startAnimation();
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void paintEvent(QPaintEvent *e) {
|
||||
Painter p(this);
|
||||
p.drawPixmap(0, 0, _cache);
|
||||
}
|
||||
|
||||
private:
|
||||
void startAnimation() {
|
||||
_opacity.start([this] { animationCallback(); }, _hiding ? 1. : 0., _hiding ? 0. : 1., st::notifyFastAnim);
|
||||
}
|
||||
void animationCallback() {
|
||||
setWindowOpacity(_opacity.current(_hiding ? 0. : 1.));
|
||||
if (!_opacity.animating() && _hiding) {
|
||||
if (_owner) {
|
||||
_owner->removeSample(this);
|
||||
}
|
||||
hide();
|
||||
deleteLater();
|
||||
}
|
||||
}
|
||||
|
||||
NotificationsBox *_owner;
|
||||
QPixmap _cache;
|
||||
FloatAnimation _opacity;
|
||||
bool _hiding = false;
|
||||
|
||||
};
|
||||
|
||||
NotificationsBox::NotificationsBox() : AbstractBox()
|
||||
, _chosenCorner(Global::NotificationsCorner())
|
||||
, _oldCount(snap(Global::NotificationsCount(), 1, kMaxNotificationsCount))
|
||||
, _countSlider(this)
|
||||
, _save(this, lang(lng_settings_save), st::defaultBoxButton)
|
||||
, _cancel(this, lang(lng_cancel), st::cancelBoxButton) {
|
||||
, _done(this, lang(lng_about_done), st::defaultBoxButton) {
|
||||
_sampleOpacities.reserve(kMaxNotificationsCount);
|
||||
for (int i = 0; i != kMaxNotificationsCount; ++i) {
|
||||
_countSlider->addSection(QString::number(i + 1));
|
||||
_sampleOpacities.push_back(FloatAnimation());
|
||||
}
|
||||
_countSlider->setActiveSectionFast(2);
|
||||
_countSlider->setActiveSectionFast(_oldCount - 1);
|
||||
_countSlider->setSectionActivatedCallback([this] { countChanged(); });
|
||||
|
||||
setMouseTracking(true);
|
||||
_save->setClickedCallback([this] {
|
||||
|
||||
});
|
||||
connect(_cancel, SIGNAL(clicked()), this, SLOT(onClose()));
|
||||
_done->setClickedCallback([this] { onClose(); });
|
||||
|
||||
prepareNotificationSampleSmall();
|
||||
prepareNotificationSampleLarge();
|
||||
setMaxHeight(st::notificationsBoxHeight);
|
||||
|
||||
prepare();
|
||||
@@ -62,17 +128,55 @@ void NotificationsBox::paintEvent(QPaintEvent *e) {
|
||||
p.setPen(st::boxTitleFg);
|
||||
p.drawTextLeft(contentLeft, st::boxTitlePosition.y(), width(), lang(lng_settings_notifications_position));
|
||||
|
||||
auto screenLeft = (width() - st::notificationsBoxScreenSize.width()) / 2;
|
||||
auto screenRect = getScreenRect();
|
||||
p.fillRect(screenRect.x(), screenRect.y(), st::notificationsBoxScreenSize.width(), st::notificationsBoxScreenSize.height(), st::notificationsBoxScreenBg);
|
||||
|
||||
auto monitorTop = st::notificationsBoxMonitorTop;
|
||||
st::notificationsBoxMonitor.paint(p, contentLeft, monitorTop, width());
|
||||
|
||||
for (int corner = 0; corner != 4; ++corner) {
|
||||
auto screenCorner = static_cast<Notify::ScreenCorner>(corner);
|
||||
auto isLeft = Notify::IsLeftCorner(screenCorner);
|
||||
auto isTop = Notify::IsTopCorner(screenCorner);
|
||||
auto sampleLeft = isLeft ? (screenRect.x() + st::notificationsSampleSkip) : (screenRect.x() + screenRect.width() - st::notificationsSampleSkip - st::notificationSampleSize.width());
|
||||
auto sampleTop = isTop ? (screenRect.y() + st::notificationsSampleTopSkip) : (screenRect.y() + screenRect.height() - st::notificationsSampleBottomSkip - st::notificationSampleSize.height());
|
||||
if (corner == static_cast<int>(_chosenCorner)) {
|
||||
auto count = currentCount();
|
||||
for (int i = 0; i != kMaxNotificationsCount; ++i) {
|
||||
auto opacity = _sampleOpacities[i].current(getms(), (i < count) ? 1. : 0.);
|
||||
p.setOpacity(opacity);
|
||||
p.drawPixmapLeft(sampleLeft, sampleTop, width(), _notificationSampleSmall);
|
||||
sampleTop += (isTop ? 1 : -1) * (st::notificationSampleSize.height() + st::notificationsSampleMargin);
|
||||
}
|
||||
} else {
|
||||
p.setOpacity(st::notificationSampleOpacity);
|
||||
p.drawPixmapLeft(sampleLeft, sampleTop, width(), _notificationSampleSmall);
|
||||
p.setOpacity(1.);
|
||||
}
|
||||
}
|
||||
|
||||
auto labelTop = screenRect.y() + screenRect.height() + st::notificationsBoxCountLabelTop;
|
||||
p.drawTextLeft(contentLeft, labelTop, width(), lang(lng_settings_notifications_count));
|
||||
}
|
||||
|
||||
void NotificationsBox::countChanged() {
|
||||
auto count = currentCount();
|
||||
auto moreSamples = (count > _oldCount);
|
||||
auto from = moreSamples ? 0. : 1.;
|
||||
auto to = moreSamples ? 1. : 0.;
|
||||
auto indexDelta = moreSamples ? 1 : -1;
|
||||
auto animatedDelta = moreSamples ? 0 : -1;
|
||||
for (; _oldCount != count; _oldCount += indexDelta) {
|
||||
_sampleOpacities[_oldCount + animatedDelta].start([this] { update(); }, from, to, st::notifyFastAnim);
|
||||
}
|
||||
|
||||
if (currentCount() != Global::NotificationsCount()) {
|
||||
Global::SetNotificationsCount(currentCount());
|
||||
Global::RefNotifySettingsChanged().notify(Notify::ChangeType::MaxCount);
|
||||
Local::writeUserSettings();
|
||||
}
|
||||
}
|
||||
|
||||
int NotificationsBox::getContentLeft() const {
|
||||
return (width() - st::notificationsBoxMonitor.width()) / 2;
|
||||
}
|
||||
@@ -84,8 +188,7 @@ QRect NotificationsBox::getScreenRect() const {
|
||||
}
|
||||
|
||||
void NotificationsBox::resizeEvent(QResizeEvent *e) {
|
||||
_save->moveToRight(st::boxButtonPadding.right(), height() - st::boxButtonPadding.bottom() - _save->height());
|
||||
_cancel->moveToRight(st::boxButtonPadding.right() + _save->width() + st::boxButtonPadding.left(), _save->y());
|
||||
_done->moveToRight(st::boxButtonPadding.right(), height() - st::boxButtonPadding.bottom() - _done->height());
|
||||
|
||||
auto screenRect = getScreenRect();
|
||||
auto sliderTop = screenRect.y() + screenRect.height() + st::notificationsBoxCountLabelTop + st::notificationsBoxCountTop;
|
||||
@@ -95,14 +198,201 @@ void NotificationsBox::resizeEvent(QResizeEvent *e) {
|
||||
AbstractBox::resizeEvent(e);
|
||||
}
|
||||
|
||||
void NotificationsBox::mousePressEvent(QMouseEvent *e) {
|
||||
void NotificationsBox::prepareNotificationSampleSmall() {
|
||||
auto width = st::notificationSampleSize.width();
|
||||
auto height = st::notificationSampleSize.height();
|
||||
auto sampleImage = QImage(width * cIntRetinaFactor(), height * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied);
|
||||
sampleImage.setDevicePixelRatio(cRetinaFactor());
|
||||
sampleImage.fill(st::notifyBg->c);
|
||||
{
|
||||
Painter p(&sampleImage);
|
||||
p.setPen(Qt::NoPen);
|
||||
p.setRenderHint(QPainter::HighQualityAntialiasing);
|
||||
|
||||
auto padding = height / 8;
|
||||
auto userpicSize = height - 2 * padding;
|
||||
p.setBrush(st::notificationSampleUserpicFg);
|
||||
p.drawEllipse(rtlrect(padding, padding, userpicSize, userpicSize, width));
|
||||
|
||||
auto rowLeft = height;
|
||||
auto rowHeight = padding;
|
||||
auto nameTop = (height - 5 * padding) / 2;
|
||||
auto nameWidth = height;
|
||||
p.setBrush(st::notificationSampleNameFg);
|
||||
p.drawRoundedRect(rtlrect(rowLeft, nameTop, nameWidth, rowHeight, width), rowHeight / 2, rowHeight / 2);
|
||||
|
||||
auto rowWidth = (width - rowLeft - 3 * padding);
|
||||
auto rowTop = nameTop + rowHeight + padding;
|
||||
p.setBrush(st::notificationSampleTextFg);
|
||||
p.drawRoundedRect(rtlrect(rowLeft, rowTop, rowWidth, rowHeight, width), rowHeight / 2, rowHeight / 2);
|
||||
rowTop += rowHeight + padding;
|
||||
p.drawRoundedRect(rtlrect(rowLeft, rowTop, rowWidth, rowHeight, width), rowHeight / 2, rowHeight / 2);
|
||||
|
||||
auto closeLeft = width - 2 * padding;
|
||||
p.fillRect(rtlrect(closeLeft, padding, padding, padding, width), st::notificationSampleCloseFg);
|
||||
}
|
||||
_notificationSampleSmall = App::pixmapFromImageInPlace(std_::move(sampleImage));
|
||||
_notificationSampleSmall.setDevicePixelRatio(cRetinaFactor());
|
||||
}
|
||||
|
||||
void NotificationsBox::prepareNotificationSampleLarge() {
|
||||
int w = st::notifyWidth, h = st::notifyMinHeight;
|
||||
auto sampleImage = QImage(w * cIntRetinaFactor(), h * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied);
|
||||
sampleImage.setDevicePixelRatio(cRetinaFactor());
|
||||
sampleImage.fill(st::notifyBg->c);
|
||||
{
|
||||
Painter p(&sampleImage);
|
||||
p.fillRect(0, 0, w - st::notifyBorderWidth, st::notifyBorderWidth, st::notifyBorder->b);
|
||||
p.fillRect(w - st::notifyBorderWidth, 0, st::notifyBorderWidth, h - st::notifyBorderWidth, st::notifyBorder->b);
|
||||
p.fillRect(st::notifyBorderWidth, h - st::notifyBorderWidth, w - st::notifyBorderWidth, st::notifyBorderWidth, st::notifyBorder->b);
|
||||
p.fillRect(0, st::notifyBorderWidth, st::notifyBorderWidth, h - st::notifyBorderWidth, st::notifyBorder->b);
|
||||
|
||||
static QPixmap icon = App::pixmapFromImageInPlace(App::wnd()->iconLarge().scaled(st::notifyPhotoSize, st::notifyPhotoSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
|
||||
p.drawPixmap(st::notifyPhotoPos.x(), st::notifyPhotoPos.y(), icon);
|
||||
|
||||
int itemWidth = w - st::notifyPhotoPos.x() - st::notifyPhotoSize - st::notifyTextLeft - st::notifyClosePos.x() - st::notifyClose.width;
|
||||
|
||||
auto rectForName = rtlrect(st::notifyPhotoPos.x() + st::notifyPhotoSize + st::notifyTextLeft, st::notifyTextTop, itemWidth, st::msgNameFont->height, w);
|
||||
|
||||
static QString notifyText = st::dialogsTextFont->elided(lang(lng_notification_preview), itemWidth);
|
||||
p.setFont(st::dialogsTextFont);
|
||||
p.setPen(st::dialogsTextFgService);
|
||||
p.drawText(st::notifyPhotoPos.x() + st::notifyPhotoSize + st::notifyTextLeft, st::notifyItemTop + st::msgNameFont->height + st::dialogsTextFont->ascent, notifyText);
|
||||
|
||||
p.setPen(st::dialogsNameFg);
|
||||
p.setFont(st::msgNameFont);
|
||||
|
||||
static QString notifyTitle = st::msgNameFont->elided(qsl("Telegram Desktop"), rectForName.width());
|
||||
p.drawText(rectForName.left(), rectForName.top() + st::msgNameFont->ascent, notifyTitle);
|
||||
}
|
||||
|
||||
_notificationSampleLarge = App::pixmapFromImageInPlace(std_::move(sampleImage));
|
||||
}
|
||||
|
||||
void NotificationsBox::removeSample(SampleWidget *widget) {
|
||||
for (auto &samples : _cornerSamples) {
|
||||
for (int i = 0, size = samples.size(); i != size; ++i) {
|
||||
if (samples[i] == widget) {
|
||||
for (int j = i + 1; j != size; ++j) {
|
||||
samples[j]->detach();
|
||||
}
|
||||
samples.resize(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NotificationsBox::mouseMoveEvent(QMouseEvent *e) {
|
||||
auto screenRect = getScreenRect();
|
||||
auto cornerWidth = screenRect.width() / 3;
|
||||
auto cornerHeight = screenRect.height() / 3;
|
||||
auto topLeft = rtlrect(screenRect.x(), screenRect.y(), cornerWidth, cornerHeight, width());
|
||||
auto topRight = rtlrect(screenRect.x() + screenRect.width() - cornerWidth, screenRect.y(), cornerWidth, cornerHeight, width());
|
||||
auto bottomRight = rtlrect(screenRect.x() + screenRect.width() - cornerWidth, screenRect.y() + screenRect.height() - cornerHeight, cornerWidth, cornerHeight, width());
|
||||
auto bottomLeft = rtlrect(screenRect.x(), screenRect.y() + screenRect.height() - cornerHeight, cornerWidth, cornerHeight, width());
|
||||
if (topLeft.contains(e->pos())) {
|
||||
setOverCorner(Notify::ScreenCorner::TopLeft);
|
||||
} else if (topRight.contains(e->pos())) {
|
||||
setOverCorner(Notify::ScreenCorner::TopRight);
|
||||
} else if (bottomRight.contains(e->pos())) {
|
||||
setOverCorner(Notify::ScreenCorner::BottomRight);
|
||||
} else if (bottomLeft.contains(e->pos())) {
|
||||
setOverCorner(Notify::ScreenCorner::BottomLeft);
|
||||
} else {
|
||||
clearOverCorner();
|
||||
}
|
||||
}
|
||||
|
||||
void NotificationsBox::leaveEvent(QEvent *e) {
|
||||
clearOverCorner();
|
||||
}
|
||||
|
||||
void NotificationsBox::setOverCorner(Notify::ScreenCorner corner) {
|
||||
if (_isOverCorner) {
|
||||
if (corner == _overCorner) {
|
||||
return;
|
||||
}
|
||||
for_const (auto widget, _cornerSamples[static_cast<int>(_overCorner)]) {
|
||||
widget->hideFast();
|
||||
}
|
||||
} else {
|
||||
_isOverCorner = true;
|
||||
setCursor(style::cur_pointer);
|
||||
Global::SetNotificationsDemoIsShown(true);
|
||||
Global::RefNotifySettingsChanged().notify(Notify::ChangeType::DemoIsShown);
|
||||
}
|
||||
_overCorner = corner;
|
||||
|
||||
auto &samples = _cornerSamples[static_cast<int>(_overCorner)];
|
||||
auto samplesAlready = samples.size();
|
||||
auto samplesNeeded = currentCount();
|
||||
auto samplesLeave = qMin(samplesAlready, samplesNeeded);
|
||||
for (int i = 0; i != samplesLeave; ++i) {
|
||||
samples[i]->showFast();
|
||||
}
|
||||
if (samplesNeeded > samplesLeave) {
|
||||
auto r = psDesktopRect();
|
||||
auto isLeft = Notify::IsLeftCorner(_overCorner);
|
||||
auto isTop = Notify::IsTopCorner(_overCorner);
|
||||
auto sampleLeft = (isLeft == rtl()) ? (r.x() + r.width() - st::notifyWidth - st::notifyDeltaX) : (r.x() + st::notifyDeltaX);
|
||||
auto sampleTop = isTop ? (r.y() + st::notifyDeltaY) : (r.y() + r.height() - st::notifyDeltaY - st::notifyMinHeight);
|
||||
for (int i = samplesLeave; i != samplesNeeded; ++i) {
|
||||
auto widget = std_::make_unique<SampleWidget>(this, _notificationSampleLarge);
|
||||
widget->move(sampleLeft, sampleTop + (isTop ? 1 : -1) * i * (st::notifyMinHeight + st::notifyDeltaY));
|
||||
widget->showFast();
|
||||
samples.push_back(widget.release());
|
||||
}
|
||||
} else {
|
||||
for (int i = samplesLeave; i != samplesAlready; ++i) {
|
||||
samples[i]->hideFast();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NotificationsBox::clearOverCorner() {
|
||||
if (_isOverCorner) {
|
||||
_isOverCorner = false;
|
||||
setCursor(style::cur_default);
|
||||
Global::SetNotificationsDemoIsShown(false);
|
||||
Global::RefNotifySettingsChanged().notify(Notify::ChangeType::DemoIsShown);
|
||||
|
||||
for_const (auto &samples, _cornerSamples) {
|
||||
for_const (auto widget, samples) {
|
||||
widget->hideFast();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int NotificationsBox::currentCount() const {
|
||||
return _countSlider->activeSection() + 1;
|
||||
}
|
||||
|
||||
void NotificationsBox::mousePressEvent(QMouseEvent *e) {
|
||||
_isDownCorner = _isOverCorner;
|
||||
_downCorner = _overCorner;
|
||||
}
|
||||
|
||||
void NotificationsBox::mouseReleaseEvent(QMouseEvent *e) {
|
||||
auto isDownCorner = createAndSwap(_isDownCorner);
|
||||
if (isDownCorner && _isOverCorner && _downCorner == _overCorner && _downCorner != _chosenCorner) {
|
||||
_chosenCorner = _downCorner;
|
||||
update();
|
||||
|
||||
if (_chosenCorner != Global::NotificationsCorner()) {
|
||||
Global::SetNotificationsCorner(_chosenCorner);
|
||||
Global::RefNotifySettingsChanged().notify(Notify::ChangeType::Corner);
|
||||
Local::writeUserSettings();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NotificationsBox::~NotificationsBox() {
|
||||
for_const (auto &samples, _cornerSamples) {
|
||||
for_const (auto widget, samples) {
|
||||
widget->detach();
|
||||
}
|
||||
}
|
||||
clearOverCorner();
|
||||
}
|
||||
|
@@ -30,23 +30,48 @@ class DiscreteSlider;
|
||||
} // namespace Ui
|
||||
|
||||
class NotificationsBox : public AbstractBox {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
NotificationsBox();
|
||||
~NotificationsBox();
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *e) override;
|
||||
void resizeEvent(QResizeEvent *e) override;
|
||||
void mousePressEvent(QMouseEvent *e) override;
|
||||
void mouseMoveEvent(QMouseEvent *e) override;
|
||||
void leaveEvent(QEvent *e) override;
|
||||
void mouseReleaseEvent(QMouseEvent *e) override;
|
||||
|
||||
private:
|
||||
using ScreenCorner = Notify::ScreenCorner;
|
||||
void countChanged();
|
||||
void setOverCorner(ScreenCorner corner);
|
||||
void clearOverCorner();
|
||||
|
||||
class SampleWidget;
|
||||
void removeSample(SampleWidget *widget);
|
||||
|
||||
int currentCount() const;
|
||||
|
||||
QRect getScreenRect() const;
|
||||
int getContentLeft() const;
|
||||
void prepareNotificationSampleSmall();
|
||||
void prepareNotificationSampleLarge();
|
||||
|
||||
QPixmap _notificationSampleSmall;
|
||||
QPixmap _notificationSampleLarge;
|
||||
ScreenCorner _chosenCorner;
|
||||
std_::vector_of_moveable<FloatAnimation> _sampleOpacities;
|
||||
|
||||
bool _isOverCorner = false;
|
||||
ScreenCorner _overCorner = ScreenCorner::TopLeft;
|
||||
bool _isDownCorner = false;
|
||||
ScreenCorner _downCorner = ScreenCorner::TopLeft;
|
||||
|
||||
int _oldCount;
|
||||
ChildWidget<Ui::DiscreteSlider> _countSlider;
|
||||
ChildWidget<BoxButton> _save, _cancel;
|
||||
ChildWidget<BoxButton> _done;
|
||||
|
||||
QVector<SampleWidget*> _cornerSamples[4];
|
||||
|
||||
};
|
||||
|
Reference in New Issue
Block a user