From 043cba0a6411db8a7b4fe5c77ed5ee81310b4486 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 4 Oct 2016 21:18:08 +0300 Subject: [PATCH] Custom notifications inline reply added, positioning broken. --- Telegram/Resources/basic.style | 25 +- .../Resources/icons/fade_horizontal_left.png | Bin 0 -> 139 bytes .../icons/fade_horizontal_left@2x.png | Bin 0 -> 192 bytes .../Resources/icons/fade_horizontal_right.png | Bin 0 -> 142 bytes .../icons/fade_horizontal_right@2x.png | Bin 0 -> 190 bytes .../Resources/icons/notification_send.png | Bin 0 -> 292 bytes .../Resources/icons/notification_send@2x.png | Bin 0 -> 506 bytes Telegram/Resources/langs/lang.strings | 2 + Telegram/SourceFiles/dialogswidget.cpp | 4 +- .../history/field_autocomplete.cpp | 4 +- Telegram/SourceFiles/media/media_audio.cpp | 4 +- .../linux/notifications_manager_linux.cpp | 2 +- .../platform/mac/notifications_manager_mac.mm | 1 + Telegram/SourceFiles/stickers/emoji_pan.cpp | 6 +- .../window/notifications_manager_default.cpp | 216 ++++++++++++++---- .../window/notifications_manager_default.h | 43 +++- .../window/notifications_utilities.cpp | 1 + Telegram/SourceFiles/window/window.style | 69 ++++++ Telegram/gyp/Telegram.gyp | 3 + 19 files changed, 300 insertions(+), 80 deletions(-) create mode 100644 Telegram/Resources/icons/fade_horizontal_left.png create mode 100644 Telegram/Resources/icons/fade_horizontal_left@2x.png create mode 100644 Telegram/Resources/icons/fade_horizontal_right.png create mode 100644 Telegram/Resources/icons/fade_horizontal_right@2x.png create mode 100644 Telegram/Resources/icons/notification_send.png create mode 100644 Telegram/Resources/icons/notification_send@2x.png create mode 100644 Telegram/SourceFiles/window/window.style diff --git a/Telegram/Resources/basic.style b/Telegram/Resources/basic.style index 3685b2be2..e5c8eae10 100644 --- a/Telegram/Resources/basic.style +++ b/Telegram/Resources/basic.style @@ -1400,15 +1400,7 @@ btnCancelSearch: iconedButton(btnAddContact) { downIcon: sprite(188px, 43px, 18px, 18px); } -notifyBG: white; -notifyBorder: #f1f1f1; -notifyBorderWidth: 1px; -notifySlowHide: 4000; -notifyPhotoSize: 62px; -notifyMacPhotoSize: 64px; -notifyPhotoPos: point(9px, 9px); -notifyClosePos: point(1px, 2px); -notifyClose: iconedButton(btnDefIconed) { +simpleClose: iconedButton(btnDefIconed) { icon: sprite(167px, 130px, 10px, 10px); iconPos: point(10px, 10px); downIcon: sprite(167px, 130px, 10px, 10px); @@ -1417,17 +1409,6 @@ notifyClose: iconedButton(btnDefIconed) { width: 30px; height: 30px; } -notifyItemTop: 12px; -notifyTextLeft: 12px; -notifyTextTop: 7px; -notifySlowHideFunc: transition(easeInCirc); -notifyWaitShortHide: 0; -notifyWaitLongHide: 20000; -notifyFastAnim: 150; -notifyWidth: 316px; -notifyHeight: 80px; -notifyDeltaX: 6px; -notifyDeltaY: 7px; boxPhotoPadding: margins(28px, 28px, 28px, 18px); boxPhotoCompressedPadding: margins(0px, 2px, 0px, 22px); @@ -2031,7 +2012,7 @@ sessionInfoFont: msgFont; sessionInfoColor: #888888; sessionTerminateTop: 30px; sessionTerminateSkip: 18px; -sessionTerminate: iconedButton(notifyClose) { +sessionTerminate: iconedButton(simpleClose) { iconPos: point(3px, 3px); downIconPos: point(3px, 4px); width: 16px; @@ -2045,6 +2026,8 @@ webPageDescriptionFont: normalFont; webPagePhotoSize: 100px; webPagePhotoDelta: 8px; +mediaPlayerSuppressDuration: 150; + botDescSkip: 8px; suppressAll: 0.2; diff --git a/Telegram/Resources/icons/fade_horizontal_left.png b/Telegram/Resources/icons/fade_horizontal_left.png new file mode 100644 index 0000000000000000000000000000000000000000..93db9ad4ce371f39fd63c99c8af186b27e4815ff GIT binary patch literal 139 zcmeAS@N?(olHy`uVBq!ia0vp^8bHj*!3HGP#J-sfq=GzM978x}hW0q}9Wmfxdij68 z^linmrs|u$Axi`gw=Ul;z5mbF-TIRhIG@XS9J=>ADD;F+irOl!V!sJ{e1!xH3(H+t o&b-MpX7=nc+;FXFcfDQQ=Js6S4_7PFfu=Kfy85}Sb4q9e0Mq_4%K!iX literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/fade_horizontal_left@2x.png b/Telegram/Resources/icons/fade_horizontal_left@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..d4e9f55a11f2b970b3267ce076226729d3ba69cd GIT binary patch literal 192 zcmeAS@N?(olHy`uVBq!ia0vp^0YJ>e!3HGHhernjsdi5n#}JK)Qv;*9SPVJ3_x^9K zO|utEUA8$^!qMbdm`96BVaMDopQ57UDuJQfXU**2m+j(n_RPs+@9b`znK0A9*o8%j z=^dZ(Y+GaLxX||f``*r6rDU96z4!8+uh;rkbG=rPW9T?x|EqKkkL6uuPC?1!UG>s` r-(L-3h{&#WjqsPOJ6pDH)dxoQ%y&FydB<-7oy6ej>gTe~DWM4fMWINA literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/fade_horizontal_right.png b/Telegram/Resources/icons/fade_horizontal_right.png new file mode 100644 index 0000000000000000000000000000000000000000..f10b4f924644713a9cc976b8405651cbce2ca3ee GIT binary patch literal 142 zcmeAS@N?(olHy`uVBq!ia0vp^8bHj*!3HGP#J-sfq(VJi978x}`kt}oV^-i{xp=T4 zvVO<^!pT}1M=xj0mVNhcqKk_{=c-3@4(}EBEHMvtO}lhA<=JifNk9H~aD2PB!}{I1 sd$YG*OHMmuX=r?=$M6{Q9;O<`Yp0q28NX8!1X{r0>FVdQ&MBb@02OIAv;Y7A literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/fade_horizontal_right@2x.png b/Telegram/Resources/icons/fade_horizontal_right@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..ed229a5b7903aa815c482fd391469ed046add8b8 GIT binary patch literal 190 zcmV;v073tWP)F|38d-s1jwi&Y_kNwrsH&Nx3^nf+VU65pmIDjK!-8nTavR!OP4;X3x!8 z)1TSQyzHtfkX*TWPeiODiFvoXBO+EevkTw*UI1?Ojqyzu;%-KKdfARLm2fhs?{hwP+O#lD@07*qoM6N<$g7qI!xBvhE literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/notification_send.png b/Telegram/Resources/icons/notification_send.png new file mode 100644 index 0000000000000000000000000000000000000000..f7f6bd47d8b536ffb5c0339c5fe6c5ceaf21ab03 GIT binary patch literal 292 zcmV+<0o(qGP)2!8?oB%yawA#&#c0000AN^}X0#P2@bJ7M>lb<@e|g8~0N ze0mN#4vxno&1N%dwORzg-f_8Hh=?eTV_Ga0)a`cbmnss~D5Yq#+0bM%sb8*0G>eqd zo?6_s%n-*h&FAxaYw?We?^=w6s_0mabWobyVhrIc7Mm)LH%P)a>7`UuYX-J2+rMv^25!w^9bAW4$)qD54Z zhEfXq{T`m@!7(AJaU2JZadWWcpF literal 0 HcmV?d00001 diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 451ccb398..b1e8b1ff7 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -872,6 +872,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org "lng_send_image_too_large" = "Could not send a file, because it is larger than 1.5 GB :("; "lng_send_folder" = "Could not send «{name}» because it is a directory :("; +"lng_notification_reply" = "Reply"; + "lng_forward_choose" = "Choose recipient..."; "lng_forward_cant" = "Sorry, no way to forward here :("; "lng_forward_confirm" = "Forward to {recipient}?"; diff --git a/Telegram/SourceFiles/dialogswidget.cpp b/Telegram/SourceFiles/dialogswidget.cpp index 3b94f1d27..66778d0f8 100644 --- a/Telegram/SourceFiles/dialogswidget.cpp +++ b/Telegram/SourceFiles/dialogswidget.cpp @@ -123,8 +123,8 @@ void DialogsInner::paintRegion(Painter &p, const QRegion ®ion, bool paintingO p.fillRect(0, 0, w, st::mentionHeight, (selected ? st::mentionBgOver : st::white)->b); if (!paintingOther) { if (selected) { - int skip = (st::mentionHeight - st::notifyClose.icon.pxHeight()) / 2; - p.drawSprite(QPoint(w - st::notifyClose.icon.pxWidth() - skip, skip), st::notifyClose.icon); + int skip = (st::mentionHeight - st::simpleClose.icon.pxHeight()) / 2; + p.drawSprite(QPoint(w - st::simpleClose.icon.pxWidth() - skip, skip), st::simpleClose.icon); } QString first = (_hashtagFilter.size() < 2) ? QString() : ('#' + _hashtagResults.at(from).mid(0, _hashtagFilter.size() - 1)), second = (_hashtagFilter.size() < 2) ? ('#' + _hashtagResults.at(from)) : _hashtagResults.at(from).mid(_hashtagFilter.size() - 1); int32 firstwidth = st::mentionFont->width(first), secondwidth = st::mentionFont->width(second); diff --git a/Telegram/SourceFiles/history/field_autocomplete.cpp b/Telegram/SourceFiles/history/field_autocomplete.cpp index df5f5a7b6..5ef830b47 100644 --- a/Telegram/SourceFiles/history/field_autocomplete.cpp +++ b/Telegram/SourceFiles/history/field_autocomplete.cpp @@ -600,9 +600,9 @@ void FieldAutocompleteInner::paintEvent(QPaintEvent *e) { bool selected = (i == _sel); if (selected) { p.fillRect(0, i * st::mentionHeight, width(), st::mentionHeight, st::mentionBgOver->b); - int skip = (st::mentionHeight - st::notifyClose.icon.pxHeight()) / 2; + int skip = (st::mentionHeight - st::simpleClose.icon.pxHeight()) / 2; if (!_hrows->isEmpty() || (!_mrows->isEmpty() && i < _recentInlineBotsInRows)) { - p.drawSprite(QPoint(width() - st::notifyClose.icon.pxWidth() - skip, i * st::mentionHeight + skip), st::notifyClose.icon); + p.drawSprite(QPoint(width() - st::simpleClose.icon.pxWidth() - skip, i * st::mentionHeight + skip), st::simpleClose.icon); } } p.setPen(st::black->p); diff --git a/Telegram/SourceFiles/media/media_audio.cpp b/Telegram/SourceFiles/media/media_audio.cpp index 8a3854e08..e947181b1 100644 --- a/Telegram/SourceFiles/media/media_audio.cpp +++ b/Telegram/SourceFiles/media/media_audio.cpp @@ -984,13 +984,13 @@ void AudioPlayerFader::onTimer() { } else if (ms > _suppressAllStart + notifyLengthMs - AudioFadeDuration) { if (_suppressAllGain.to() != 1.) _suppressAllGain.start(1.); _suppressAllGain.update(1. - ((_suppressAllStart + notifyLengthMs - ms) / float64(AudioFadeDuration)), anim::linear); - } else if (ms >= _suppressAllStart + st::notifyFastAnim) { + } else if (ms >= _suppressAllStart + st::mediaPlayerSuppressDuration) { if (_suppressAllAnim) { _suppressAllGain.finish(); _suppressAllAnim = false; } } else if (ms > _suppressAllStart) { - _suppressAllGain.update((ms - _suppressAllStart) / st::notifyFastAnim, anim::linear); + _suppressAllGain.update((ms - _suppressAllStart) / st::mediaPlayerSuppressDuration, anim::linear); } suppressAllGain = _suppressAllGain.current(); suppressAudioChanged = (suppressAllGain != wasAudio); diff --git a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp index 3e0fd4fce..6ff08d77a 100644 --- a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp @@ -156,7 +156,7 @@ private: if (auto manager = ManagerInstance.data()) { if (manager->hasActionsSupport()) { - auto label = lang(lng_context_reply_msg).toUtf8(); + auto label = lang(lng_notification_reply).toUtf8(); auto actionReceiver = _data; auto actionHandler = &NotificationData::notificationClicked; auto actionLabel = label.constData(); diff --git a/Telegram/SourceFiles/platform/mac/notifications_manager_mac.mm b/Telegram/SourceFiles/platform/mac/notifications_manager_mac.mm index 831fce549..c92db9c02 100644 --- a/Telegram/SourceFiles/platform/mac/notifications_manager_mac.mm +++ b/Telegram/SourceFiles/platform/mac/notifications_manager_mac.mm @@ -23,6 +23,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "pspecific.h" #include "platform/mac/mac_utilities.h" +#include "styles/style_window.h" #include diff --git a/Telegram/SourceFiles/stickers/emoji_pan.cpp b/Telegram/SourceFiles/stickers/emoji_pan.cpp index d515e3506..900ea04da 100644 --- a/Telegram/SourceFiles/stickers/emoji_pan.cpp +++ b/Telegram/SourceFiles/stickers/emoji_pan.cpp @@ -2419,14 +2419,14 @@ EmojiPanel::EmojiPanel(QWidget *parent, const QString &text, uint64 setId, bool , _setId(setId) , _special(special) , _deleteVisible(false) -, _delete(special ? 0 : new IconedButton(this, st::notifyClose)) { // Stickers::NoneSetId if in emoji +, _delete(special ? 0 : new IconedButton(this, st::simpleClose)) { // Stickers::NoneSetId if in emoji resize(st::emojiPanWidth, st::emojiPanHeader); setMouseTracking(true); setFocusPolicy(Qt::NoFocus); setText(text); if (_delete) { _delete->hide(); - _delete->moveToRight(st::emojiPanHeaderLeft - ((_delete->width() - st::notifyClose.icon.pxWidth()) / 2), (st::emojiPanHeader - _delete->height()) / 2, width()); + _delete->moveToRight(st::emojiPanHeaderLeft - ((_delete->width() - st::simpleClose.icon.pxWidth()) / 2), (st::emojiPanHeader - _delete->height()) / 2, width()); connect(_delete, SIGNAL(clicked()), this, SLOT(onDelete())); } } @@ -2444,7 +2444,7 @@ void EmojiPanel::updateText() { int32 availw = st::emojiPanWidth - st::emojiPanHeaderLeft * 2; if (_deleteVisible) { if (!_special && _setId != Stickers::NoneSetId) { - availw -= st::notifyClose.icon.pxWidth() + st::emojiPanHeaderLeft; + availw -= st::simpleClose.icon.pxWidth() + st::emojiPanHeaderLeft; } } else { auto switchText = ([this]() { diff --git a/Telegram/SourceFiles/window/notifications_manager_default.cpp b/Telegram/SourceFiles/window/notifications_manager_default.cpp index 5d331b4b7..f0b57d975 100644 --- a/Telegram/SourceFiles/window/notifications_manager_default.cpp +++ b/Telegram/SourceFiles/window/notifications_manager_default.cpp @@ -24,8 +24,10 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "platform/platform_notifications_manager.h" #include "mainwindow.h" #include "lang.h" +#include "ui/buttons/icon_button.h" #include "dialogs/dialogs_layout.h" #include "styles/style_dialogs.h" +#include "styles/style_window.h" namespace Window { namespace Notifications { @@ -37,6 +39,16 @@ constexpr int kNotifyWindowsCount = 3; NeverFreedPointer ManagerInstance; +int notificationWidth() { + static auto result = ([] { + auto replyWidth = st::defaultBoxButton.font->width(lang(lng_notification_reply).toUpper()) - st::defaultBoxButton.width; + auto textLeft = st::notifyPhotoPos.x() + st::notifyPhotoSize + st::notifyTextLeft; + auto minWidth = textLeft + replyWidth + st::boxButtonPadding.right(); + return qMax(st::notifyMinWidth, minWidth); + })(); + return result; +} + } // namespace void start() { @@ -78,7 +90,7 @@ void Manager::showNextFromQueue() { int count = kNotifyWindowsCount; for_const (auto widget, _widgets) { - if (widget->index() < 0) continue; + if (widget->isUnlinked()) continue; --count; } if (count <= 0) { @@ -86,8 +98,8 @@ void Manager::showNextFromQueue() { } auto r = psDesktopRect(); - auto x = r.x() + r.width() - st::notifyWidth - st::notifyDeltaX; - auto y = r.y() + r.height() - st::notifyHeight - st::notifyDeltaY; + auto x = r.x() + r.width() - notificationWidth() - st::notifyDeltaX; + auto y = r.y() + r.height(); do { auto queued = _queuedNotifications.front(); _queuedNotifications.pop_front(); @@ -98,11 +110,11 @@ void Manager::showNextFromQueue() { --count; } while (count > 0 && !_queuedNotifications.isEmpty()); - auto shown = kNotifyWindowsCount - count; + auto bottom = y - st::notifyDeltaY; for_const (auto widget, _widgets) { - if (widget->index() < 0) continue; - --shown; - widget->moveTo(x, y - shown * (st::notifyHeight + st::notifyDeltaY)); + if (widget->isUnlinked() < 0) continue; + widget->moveTop(y - widget->height(), y); + y -= widget->height() + st::notifyDeltaY; } } @@ -177,12 +189,16 @@ Widget::Widget(History *history, PeerData *peer, PeerData *author, HistoryItem * , _started(GetTickCount()) #endif // Q_OS_WIN && !Q_OS_WINRT , _close(this, st::notifyClose) +, _reply(this, lang(lng_notification_reply), st::defaultBoxButton) , _alphaDuration(st::notifyFastAnim) -, _posDuration(st::notifyFastAnim) , a_opacity(0) , a_func(anim::linear) -, a_y(y + st::notifyHeight + st::notifyDeltaY) +, a_top(y) +, a_bottom(y + st::notifyMinHeight) +, _a_movement(animation(this, &Widget::step_movement)) , _a_appearance(animation(this, &Widget::step_appearance)) { + setGeometry(x, a_top.current(), notificationWidth(), a_bottom.current() - a_top.current()); + updateNotifyDisplay(); _hideTimer.setSingleShot(true); @@ -191,15 +207,21 @@ Widget::Widget(History *history, PeerData *peer, PeerData *author, HistoryItem * _inputTimer.setSingleShot(true); connect(&_inputTimer, SIGNAL(timeout()), this, SLOT(checkLastInput())); - _close.setClickedCallback([this] { + _close->setClickedCallback([this] { unlinkHistoryAndNotify(); }); - _close.setAcceptBoth(true); - _close.move(st::notifyWidth - st::notifyClose.width - st::notifyClosePos.x(), st::notifyClosePos.y()); - _close.show(); + _close->setAcceptBoth(true); + _close->moveToRight(st::notifyClosePos.x(), st::notifyClosePos.y()); + _close->show(); - a_y.start(y); - setGeometry(x, a_y.current(), st::notifyWidth, st::notifyHeight); + _reply->setClickedCallback([this] { + showReplyField(); + }); + _replyPadding = st::notifyMinHeight - st::notifyPhotoPos.y() - st::notifyPhotoSize; + _reply->moveToRight(_replyPadding, height() - _reply->height() - _replyPadding); + _reply->hide(); + + prepareActionsCache(); a_opacity.start(1); setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint | Qt::FramelessWindowHint | Qt::X11BypassWindowManagerHint); @@ -209,12 +231,29 @@ Widget::Widget(History *history, PeerData *peer, PeerData *author, HistoryItem * setWindowOpacity(a_opacity.current()); - _alphaDuration = _posDuration = st::notifyFastAnim; + _alphaDuration = st::notifyFastAnim; _a_appearance.start(); checkLastInput(); } +void Widget::prepareActionsCache() { + auto replyCache = myGrab(_reply); + auto fadeWidth = st::notifyFadeRight.width(); + auto actionsTop = st::notifyTextTop + st::msgNameFont->height; + auto actionsCacheWidth = _reply->width() + _replyPadding + fadeWidth; + auto actionsCacheHeight = height() - actionsTop; + auto actionsCacheImg = QImage(actionsCacheWidth * cIntRetinaFactor(), actionsCacheHeight * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied); + actionsCacheImg.fill(st::transparent->c); + { + Painter p(&actionsCacheImg); + st::notifyFadeRight.fill(p, rtlrect(0, 0, fadeWidth, actionsCacheHeight, actionsCacheWidth)); + p.fillRect(rtlrect(fadeWidth, 0, actionsCacheWidth - fadeWidth, actionsCacheHeight, actionsCacheWidth), st::notifyBg); + p.drawPixmapRight(_replyPadding, _reply->y() - actionsTop, actionsCacheWidth, replyCache); + } + _buttonsCache = App::pixmapFromImageInPlace(std_::move(actionsCacheImg)); +} + void Widget::checkLastInput() { #if defined Q_OS_WIN && !defined Q_OS_WINRT LASTINPUTINFO lii; @@ -235,24 +274,39 @@ void Widget::checkLastInput() { #endif // else for Q_OS_WIN && !Q_OS_WINRT } -void Widget::moveTo(int x, int y, int index) { - if (index >= 0) { - _index = index; +void Widget::onReplyResize() { + _a_movement.stop(); + auto newHeight = st::notifyMinHeight + _replyArea->height() + st::notifyBorderWidth; + auto delta = height() - newHeight; + if (delta != 0) { + auto top = a_top.current() + delta; + a_top = anim::ivalue(top, top); + setGeometry(x(), top, width(), a_bottom.current() - top); + update(); } - move(x, a_y.current()); - a_y.start(y); - a_opacity.restart(); - _posDuration = st::notifyFastAnim; - _a_appearance.start(); +} + +void Widget::onReplySubmit(bool ctrlShiftEnter) { + sendReply(); +} + +void Widget::onReplyCancel() { + unlinkHistoryAndNotify(); +} + +void Widget::moveTop(int top, int bottom) { + a_top.start(top); + a_bottom.start(bottom); + _a_movement.start(); } void Widget::updateNotifyDisplay() { if (!_history || !_peer || (!_item && _forwardedCount < 2)) return; - int32 w = st::notifyWidth, h = st::notifyHeight; + int32 w = width(), h = height(); QImage img(w * cIntRetinaFactor(), h * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied); if (cRetina()) img.setDevicePixelRatio(cRetinaFactor()); - img.fill(st::notifyBG->c); + img.fill(st::notifyBg->c); { Painter p(&img); @@ -345,6 +399,64 @@ void Widget::unlinkHistoryAndNotify() { } } +void Widget::toggleActionButtons(bool visible) { + if (_actionsVisible != visible) { + _actionsVisible = visible; + _reply->hide(); + a_actionsOpacity.start([this] { actionsOpacityCallback(); }, _actionsVisible ? 0. : 1., _actionsVisible ? 1. : 0., st::notifyActionsDuration); + } +} + +void Widget::actionsOpacityCallback() { + update(); + if (!a_actionsOpacity.animating() && _actionsVisible) { + _reply->show(); + } +} + +void Widget::showReplyField() { + if (_replyArea) return; + + _replyArea = new InputArea(this, st::notifyReplyArea, lang(lng_message_ph), QString()); + _replyArea->resize(width() - st::notifySendReply.width - 2 * st::notifyBorderWidth, st::notifySendReply.height); + _replyArea->moveToLeft(st::notifyBorderWidth, st::notifyMinHeight); + _replyArea->show(); + _replyArea->setFocus(); + _replyArea->setMaxLength(MaxMessageSize); + _replyArea->setCtrlEnterSubmit(CtrlEnterSubmitBoth); + connect(_replyArea, SIGNAL(resized()), this, SLOT(onReplyResize())); + connect(_replyArea, SIGNAL(submitted(bool)), this, SLOT(onReplySubmit(bool))); + connect(_replyArea, SIGNAL(cancelled()), this, SLOT(onReplyCancel())); + + _replySend = new Ui::IconButton(this, st::notifySendReply); + _replySend->moveToRight(st::notifyBorderWidth, st::notifyMinHeight); + _replySend->show(); + _replySend->setClickedCallback([this] { sendReply(); }); + + toggleActionButtons(false); + + a_top.finish(); + a_bottom.finish(); + + _a_movement.stop(); + auto top = a_top.current() - _replyArea->height() - st::notifyBorderWidth; + auto bottom = a_top.current() + st::notifyMinHeight; + a_top = anim::ivalue(top, top); + a_bottom = anim::ivalue(bottom, bottom); + setGeometry(x(), top, width(), bottom - top); + update(); +} + +void Widget::sendReply() { + if (!_history) return; + + if (auto manager = ManagerInstance.data()) { + auto peerId = _history->peer->id; + auto msgId = _item ? _item->id : ShowAtUnreadMsgId; + manager->notificationReplied(peerId, msgId, _replyArea->getLastText()); + } +} + void Widget::unlinkHistory(History *history) { if (!history || history == _history) { animHide(st::notifyFastAnim, anim::linear); @@ -358,6 +470,9 @@ void Widget::enterEvent(QEvent *e) { if (auto manager = ManagerInstance.data()) { manager->stopAllHiding(); } + if (!_replyArea) { + toggleActionButtons(true); + } } void Widget::leaveEvent(QEvent *e) { @@ -365,6 +480,7 @@ void Widget::leaveEvent(QEvent *e) { if (auto manager = ManagerInstance.data()) { manager->startAllHiding(); } + toggleActionButtons(false); } void Widget::startHiding() { @@ -387,8 +503,27 @@ void Widget::mousePressEvent(QMouseEvent *e) { } void Widget::paintEvent(QPaintEvent *e) { - QPainter p(this); + Painter p(this); p.drawPixmap(0, 0, _cache); + + auto buttonsLeft = st::notifyPhotoPos.x() + st::notifyPhotoSize + st::notifyTextLeft; + auto buttonsTop = st::notifyTextTop + st::msgNameFont->height; + if (a_actionsOpacity.animating(getms())) { + p.setOpacity(a_actionsOpacity.current()); + p.drawPixmapRight(0, buttonsTop, width(), _buttonsCache); + } else if (_actionsVisible) { + p.drawPixmapRight(0, buttonsTop, width(), _buttonsCache); + } + + if (height() > st::notifyMinHeight) { + auto replyAreaHeight = height() - st::notifyMinHeight - st::notifyBorderWidth; + if (replyAreaHeight > 0) { + p.fillRect(st::notifyBorderWidth, st::notifyMinHeight, width() - 2 * st::notifyBorderWidth, replyAreaHeight, st::notifyBg); + } + p.fillRect(0, st::notifyMinHeight, st::notifyBorderWidth, height() - st::notifyMinHeight, st::notifyBorder); + p.fillRect(width() - st::notifyBorderWidth, st::notifyMinHeight, st::notifyBorderWidth, height() - st::notifyMinHeight, st::notifyBorder); + p.fillRect(st::notifyBorderWidth, height() - st::notifyBorderWidth, width() - 2 * st::notifyBorderWidth, st::notifyBorderWidth, st::notifyBorder); + } } void Widget::animHide(float64 duration, anim::transition func) { @@ -396,7 +531,6 @@ void Widget::animHide(float64 duration, anim::transition func) { _alphaDuration = duration; a_func = func; a_opacity.start(0); - a_y.restart(); _hiding = true; _a_appearance.start(); } @@ -406,7 +540,6 @@ void Widget::stopHiding() { _alphaDuration = st::notifyFastAnim; a_func = anim::linear; a_opacity.start(1); - a_y.restart(); _hiding = false; _hideTimer.stop(); _a_appearance.start(); @@ -418,25 +551,30 @@ void Widget::hideByTimer() { } void Widget::step_appearance(float64 ms, bool timer) { - float64 dtAlpha = ms / _alphaDuration, dtPos = ms / _posDuration; - if (dtAlpha >= 1) { + float64 dt = ms / float64(_alphaDuration); + if (dt >= 1) { a_opacity.finish(); + _a_appearance.stop(); if (_hiding) { - _a_appearance.stop(); deleteLater(); - } else if (dtPos >= 1) { - _a_appearance.stop(); } } else { - a_opacity.update(dtAlpha, a_func); + a_opacity.update(dt, a_func); } setWindowOpacity(a_opacity.current()); - if (dtPos >= 1) { - a_y.finish(); + update(); +} + +void Widget::step_movement(float64 ms, bool timer) { + float64 dt = ms / float64(st::notifyFastAnim); + if (dt >= 1) { + a_top.finish(); + a_bottom.finish(); } else { - a_y.update(dtPos, anim::linear); + a_top.update(dt, anim::linear); + a_bottom.update(dt, anim::linear); } - move(x(), a_y.current()); + setGeometry(x(), a_top.current(), width(), a_bottom.current() - a_top.current()); update(); } diff --git a/Telegram/SourceFiles/window/notifications_manager_default.h b/Telegram/SourceFiles/window/notifications_manager_default.h index bb1fd8789..2d2c1cfa5 100644 --- a/Telegram/SourceFiles/window/notifications_manager_default.h +++ b/Telegram/SourceFiles/window/notifications_manager_default.h @@ -22,6 +22,10 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "window/notifications_manager.h" +namespace Ui { +class IconButton; +} // namespace Ui + namespace Window { namespace Notifications { namespace Default { @@ -88,22 +92,20 @@ class Widget : public TWidget { public: Widget(History *history, PeerData *peer, PeerData *author, HistoryItem *item, int forwardedCount, int x, int y); - void step_appearance(float64 ms, bool timer); - void animHide(float64 duration, anim::transition func); void startHiding(); void stopHiding(); - void moveTo(int x, int y, int index = -1); + void moveTop(int top, int bottom); void updateNotifyDisplay(); void updatePeerPhoto(); void itemRemoved(HistoryItem *del); - int index() const { - return _history ? _index : -1; + int isUnlinked() const { + return !_history; } - void unlinkHistory(History *hist = 0); + void unlinkHistory(History *history = nullptr); ~Widget(); @@ -116,9 +118,20 @@ protected: private slots: void hideByTimer(); void checkLastInput(); + void onReplyResize(); + void onReplySubmit(bool ctrlShiftEnter); + void onReplyCancel(); private: + void animHide(float64 duration, anim::transition func); + void step_appearance(float64 ms, bool timer); + void step_movement(float64 ms, bool timer); void unlinkHistoryAndNotify(); + void toggleActionButtons(bool visible); + void prepareActionsCache(); + void actionsOpacityCallback(); + void showReplyField(); + void sendReply(); #if defined Q_OS_WIN && !defined Q_OS_WINRT uint64 _started; @@ -129,17 +142,27 @@ private: PeerData *_author; HistoryItem *_item; int _forwardedCount; - IconedButton _close; + ChildWidget _close; + ChildWidget _reply; + ChildWidget _replyArea = { nullptr }; + ChildWidget _replySend = { nullptr }; QPixmap _cache; - float64 _alphaDuration, _posDuration; + float64 _alphaDuration; QTimer _hideTimer, _inputTimer; bool _hiding = false; - int _index = 0; + + anim::ivalue a_top, a_bottom; + Animation _a_movement; + anim::fvalue a_opacity; anim::transition a_func; - anim::ivalue a_y; Animation _a_appearance; + int _replyPadding = 0; + bool _actionsVisible = false; + FloatAnimation a_actionsOpacity; + QPixmap _buttonsCache; + ImagePtr peerPhoto; }; diff --git a/Telegram/SourceFiles/window/notifications_utilities.cpp b/Telegram/SourceFiles/window/notifications_utilities.cpp index db2aae815..2d48cdd4c 100644 --- a/Telegram/SourceFiles/window/notifications_utilities.cpp +++ b/Telegram/SourceFiles/window/notifications_utilities.cpp @@ -23,6 +23,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "pspecific.h" #include "mainwindow.h" +#include "styles/style_window.h" namespace Window { namespace Notifications { diff --git a/Telegram/SourceFiles/window/window.style b/Telegram/SourceFiles/window/window.style new file mode 100644 index 000000000..2c8e3da71 --- /dev/null +++ b/Telegram/SourceFiles/window/window.style @@ -0,0 +1,69 @@ +/* +This file is part of Telegram Desktop, +the official desktop version of Telegram messaging app, see https://telegram.org + +Telegram Desktop is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +It is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +In addition, as a special exception, the copyright holders give permission +to link the code of portions of this program with the OpenSSL library. + +Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE +Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org +*/ + +using "basic.style"; + +notifyBg: white; +notifyBorder: #f1f1f1; +notifyBorderWidth: 1px; +notifySlowHide: 4000; +notifyPhotoSize: 62px; +notifyMacPhotoSize: 64px; +notifyPhotoPos: point(9px, 9px); +notifyClosePos: point(1px, 2px); +notifyClose: iconedButton(simpleClose) { +} +notifyItemTop: 12px; +notifyTextLeft: 12px; +notifyTextTop: 7px; +notifySlowHideFunc: transition(easeInCirc); +notifyWaitShortHide: 0; +notifyWaitLongHide: 20000; +notifyFastAnim: 150; +notifyMinWidth: 316px; +notifyMinHeight: 80px; +notifyDeltaX: 6px; +notifyDeltaY: 7px; +notifyActionsDuration: 200; + +notifyFadeRight: icon {{ "fade_horizontal_right", notifyBg }}; +notifyReplyArea: InputArea(defaultInputArea) { + font: normalFont; + textMargins: margins(8px, 8px, 8px, 6px); + heightMin: 36px; + heightMax: 72px; + border: 0px; + borderActive: 0px; + borderError: 0px; +} +notifySendReply: IconButton { + width: 36px; + height: 36px; + + opacity: 0.78; + overOpacity: 1.; + + icon: icon {{ "notification_send", windowActiveBg, point(3px, 9px) }}; + iconPosition: point(0px, 0px); + downIconPosition: point(0px, 1px); + + duration: notifyFastAnim; +} diff --git a/Telegram/gyp/Telegram.gyp b/Telegram/gyp/Telegram.gyp index cb4b63355..7598ad52c 100644 --- a/Telegram/gyp/Telegram.gyp +++ b/Telegram/gyp/Telegram.gyp @@ -40,6 +40,7 @@ '<(src_loc)/settings/settings.style', '<(src_loc)/stickers/stickers.style', '<(src_loc)/ui/widgets/widgets.style', + '<(src_loc)/window/window.style', ], 'langpacks': [ 'en', @@ -512,6 +513,8 @@ '<(src_loc)/pspecific_linux.h', '<(src_loc)/platform/linux/linux_gdk_helper.cpp', '<(src_loc)/platform/linux/linux_gdk_helper.h', + '<(src_loc)/platform/linux/linux_libnotify.cpp', + '<(src_loc)/platform/linux/linux_libnotify.h', '<(src_loc)/platform/linux/linux_libs.cpp', '<(src_loc)/platform/linux/linux_libs.h', '<(src_loc)/platform/linux/file_dialog_linux.cpp',