diff --git a/Telegram/SourceFiles/dropdown.cpp b/Telegram/SourceFiles/dropdown.cpp index 6d4ae62fc3..d862a660dc 100644 --- a/Telegram/SourceFiles/dropdown.cpp +++ b/Telegram/SourceFiles/dropdown.cpp @@ -1019,11 +1019,11 @@ void EmojiPanInner::leaveEvent(QEvent *e) { clearSelection(); } -void EmojiPanInner::leaveToChildEvent(QEvent *e) { +void EmojiPanInner::leaveToChildEvent(QEvent *e, QWidget *child) { clearSelection(); } -void EmojiPanInner::enterFromChildEvent(QEvent *e) { +void EmojiPanInner::enterFromChildEvent(QEvent *e, QWidget *child) { _lastMousePos = QCursor::pos(); updateSelected(); } @@ -1544,11 +1544,11 @@ void StickerPanInner::leaveEvent(QEvent *e) { clearSelection(); } -void StickerPanInner::leaveToChildEvent(QEvent *e) { +void StickerPanInner::leaveToChildEvent(QEvent *e, QWidget *child) { clearSelection(); } -void StickerPanInner::enterFromChildEvent(QEvent *e) { +void StickerPanInner::enterFromChildEvent(QEvent *e, QWidget *child) { _lastMousePos = QCursor::pos(); updateSelected(); } @@ -3139,7 +3139,7 @@ void EmojiPan::onRefreshPanels() { } } -void EmojiPan::leaveToChildEvent(QEvent *e) { +void EmojiPan::leaveToChildEvent(QEvent *e, QWidget *child) { if (!_stickersShown) return; _iconsMousePos = QCursor::pos(); updateSelected(); diff --git a/Telegram/SourceFiles/dropdown.h b/Telegram/SourceFiles/dropdown.h index ae7a33159f..32faf35687 100644 --- a/Telegram/SourceFiles/dropdown.h +++ b/Telegram/SourceFiles/dropdown.h @@ -254,14 +254,14 @@ public: EmojiPanInner(); void setMaxHeight(int32 h); - void paintEvent(QPaintEvent *e); + void paintEvent(QPaintEvent *e) override; - void mousePressEvent(QMouseEvent *e); - void mouseReleaseEvent(QMouseEvent *e); - void mouseMoveEvent(QMouseEvent *e); - void leaveEvent(QEvent *e); - void leaveToChildEvent(QEvent *e); - void enterFromChildEvent(QEvent *e); + void mousePressEvent(QMouseEvent *e) override; + void mouseReleaseEvent(QMouseEvent *e) override; + void mouseMoveEvent(QMouseEvent *e) override; + void leaveEvent(QEvent *e) override; + void leaveToChildEvent(QEvent *e, QWidget *child) override; + void enterFromChildEvent(QEvent *e, QWidget *child) override; void step_selected(uint64 ms, bool timer); void hideFinish(); @@ -346,14 +346,14 @@ public: StickerPanInner(); void setMaxHeight(int32 h); - void paintEvent(QPaintEvent *e); + void paintEvent(QPaintEvent *e) override; - void mousePressEvent(QMouseEvent *e); - void mouseReleaseEvent(QMouseEvent *e); - void mouseMoveEvent(QMouseEvent *e); - void leaveEvent(QEvent *e); - void leaveToChildEvent(QEvent *e); - void enterFromChildEvent(QEvent *e); + void mousePressEvent(QMouseEvent *e) override; + void mouseReleaseEvent(QMouseEvent *e) override; + void mouseMoveEvent(QMouseEvent *e) override; + void leaveEvent(QEvent *e) override; + void leaveToChildEvent(QEvent *e, QWidget *child) override; + void enterFromChildEvent(QEvent *e, QWidget *child) override; void step_selected(uint64 ms, bool timer); @@ -663,7 +663,7 @@ private: bool _horizontal; void updateContentHeight(); - void leaveToChildEvent(QEvent *e); + void leaveToChildEvent(QEvent *e, QWidget *child); void hideAnimated(); void prepareShowHideCache(); diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp index 9c8b6c3f95..e2018e752a 100644 --- a/Telegram/SourceFiles/historywidget.cpp +++ b/Telegram/SourceFiles/historywidget.cpp @@ -28,9 +28,11 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "ui/filedialog.h" #include "ui/toast/toast.h" #include "ui/buttons/history_down_button.h" +#include "ui/inner_dropdown.h" #include "inline_bots/inline_bot_result.h" #include "data/data_drafts.h" #include "history/history_service_layout.h" +#include "profile/profile_members_widget.h" #include "lang.h" #include "application.h" #include "mainwidget.h" @@ -5442,7 +5444,7 @@ void HistoryWidget::mouseMoveEvent(QMouseEvent *e) { if (startAnim) _a_record.start(); } -void HistoryWidget::leaveToChildEvent(QEvent *e) { // e -- from enterEvent() of child TWidget +void HistoryWidget::leaveToChildEvent(QEvent *e, QWidget *child) { // e -- from enterEvent() of child TWidget if (hasMouseTracking()) mouseMoveEvent(0); } @@ -6009,6 +6011,37 @@ void HistoryWidget::paintTopBar(Painter &p, float64 over, int32 decreaseWidth) { } } +QRect HistoryWidget::getMembersShowAreaGeometry() const { + int increaseLeft = Adaptive::OneColumn() ? (st::topBarForwardPadding.right() - st::topBarForwardPadding.left()) : 0; + int membersTextLeft = st::topBarForwardPadding.left() + increaseLeft; + int membersTextTop = st::topBarHeight - st::topBarForwardPadding.bottom() - st::dialogsTextFont->height - st::topBarForwardPadding.bottom(); + int membersTextWidth = _titlePeerTextWidth; + int membersTextHeight = st::topBarHeight - membersTextTop; + + membersTextLeft -= st::topBarForwardPadding.left(); + membersTextWidth += 2 * st::topBarForwardPadding.left(); + + return rtlrect(membersTextLeft, membersTextTop, membersTextWidth, membersTextHeight, width()); +} + +void HistoryWidget::setMembersShowAreaActive(bool active) { + if (active && _peer && (_peer->isChat() || _peer->isMegagroup())) { + if (!_membersDropdown) { + _membersDropdown = new Ui::InnerDropdown(this, st::dropdownDef, st::solidScroll); + _membersDropdown->setOwnedWidget(new Profile::MembersWidget(_membersDropdown, _peer, Profile::MembersWidget::TitleVisibility::Hidden)); + _membersDropdown->setGeometry(0, 0, st::emojiPanWidth, st::emojiPanMaxHeight); + connect(_membersDropdown, SIGNAL(hidden()), this, SLOT(onMembersDropdownHidden())); + } + _membersDropdown->otherEnter(); + } else if (_membersDropdown) { + _membersDropdown->otherLeave(); + } +} + +void HistoryWidget::onMembersDropdownHidden() { + _membersDropdown.destroyDelayed(); +} + void HistoryWidget::topBarClick() { if (Adaptive::OneColumn()) { Ui::showChatsList(); @@ -6074,6 +6107,7 @@ void HistoryWidget::updateOnlineDisplay(int32 x, int32 w) { _titlePeerTextOnline = titlePeerTextOnline; _titlePeerTextWidth = st::dialogsTextFont->width(_titlePeerText); if (App::main()) { + App::main()->topBar()->updateMembersShowArea(); App::main()->topBar()->update(); } } @@ -7263,6 +7297,9 @@ bool HistoryWidget::pinnedMsgVisibilityUpdated() { connect(&_pinnedBar->cancel, SIGNAL(clicked()), this, SLOT(onPinnedHide())); _reportSpamPanel.raise(); _topShadow.raise(); + if (_membersDropdown) { + _membersDropdown->raise(); + } updatePinnedBar(); result = true; diff --git a/Telegram/SourceFiles/historywidget.h b/Telegram/SourceFiles/historywidget.h index c1481437b0..e274de9d3a 100644 --- a/Telegram/SourceFiles/historywidget.h +++ b/Telegram/SourceFiles/historywidget.h @@ -36,6 +36,7 @@ class Result; namespace Ui { class HistoryDownButton; +class InnerDropdown; } // namespace Ui class HistoryWidget; @@ -540,12 +541,14 @@ public: void dropEvent(QDropEvent *e) override; void mouseReleaseEvent(QMouseEvent *e) override; void mouseMoveEvent(QMouseEvent *e) override; - void leaveToChildEvent(QEvent *e) override; + void leaveToChildEvent(QEvent *e, QWidget *child) override; void contextMenuEvent(QContextMenuEvent *e) override; void updateTopBarSelection(); void paintTopBar(Painter &p, float64 over, int32 decreaseWidth); + QRect getMembersShowAreaGeometry() const; + void setMembersShowAreaActive(bool active); void topBarClick(); void loadMessages(); @@ -839,6 +842,7 @@ private slots: void onHashtagOrBotCommandInsert(QString str, FieldAutocomplete::ChooseMethod method); void onMentionInsert(UserData *user); void onInlineBotCancel(); + void onMembersDropdownHidden(); void updateField(); @@ -1099,6 +1103,8 @@ private: ScrollArea _kbScroll; BotKeyboard _keyboard; + ChildWidget _membersDropdown = { nullptr }; + Dropdown _attachType; EmojiPan _emojiPan; DragState _attachDrag = DragStateNone; diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index 8b48d365ab..9439a61dfc 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -2696,6 +2696,19 @@ void MainWidget::paintTopBar(Painter &p, float64 over, int32 decreaseWidth) { } } +QRect MainWidget::getMembersShowAreaGeometry() const { + if (!_overview && !_wideSection) { + return _history->getMembersShowAreaGeometry(); + } + return QRect(); +} + +void MainWidget::setMembersShowAreaActive(bool active) { + if (!active || (!_overview && !_wideSection)) { + _history->setMembersShowAreaActive(active); + } +} + void MainWidget::onPhotosSelect() { if (_overview) _overview->switchType(OverviewPhotos); _mediaType->hideStart(); diff --git a/Telegram/SourceFiles/mainwidget.h b/Telegram/SourceFiles/mainwidget.h index 62a85bab97..7b57d47425 100644 --- a/Telegram/SourceFiles/mainwidget.h +++ b/Telegram/SourceFiles/mainwidget.h @@ -162,7 +162,10 @@ public: void updateAdaptiveLayout(); bool needBackButton(); + // Temporary methods, while top bar was not done inside HistoryWidget / OverviewWidget. void paintTopBar(Painter &p, float64 over, int32 decreaseWidth); + QRect getMembersShowAreaGeometry() const; + void setMembersShowAreaActive(bool active); Window::TopBarWidget *topBar(); PlayerWidget *player(); diff --git a/Telegram/SourceFiles/mediaview.h b/Telegram/SourceFiles/mediaview.h index 8045e7a22c..ec34e421c1 100644 --- a/Telegram/SourceFiles/mediaview.h +++ b/Telegram/SourceFiles/mediaview.h @@ -51,10 +51,10 @@ public: bool moveToNext(int32 delta); void preloadData(int32 delta); - void leaveToChildEvent(QEvent *e) override { // e -- from enterEvent() of child TWidget + void leaveToChildEvent(QEvent *e, QWidget *child) override { // e -- from enterEvent() of child TWidget updateOverState(OverNone); } - void enterFromChildEvent(QEvent *e) override { // e -- from leaveEvent() of child TWidget + void enterFromChildEvent(QEvent *e, QWidget *child) override { // e -- from leaveEvent() of child TWidget updateOver(mapFromGlobal(QCursor::pos())); } diff --git a/Telegram/SourceFiles/profile/profile_block_widget.cpp b/Telegram/SourceFiles/profile/profile_block_widget.cpp index ba2c8ac5f0..f6d108fa79 100644 --- a/Telegram/SourceFiles/profile/profile_block_widget.cpp +++ b/Telegram/SourceFiles/profile/profile_block_widget.cpp @@ -25,29 +25,30 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org namespace Profile { -BlockWidget::BlockWidget(QWidget *parent, PeerData *peer, const QString &title) : TWidget(parent) +BlockWidget::BlockWidget(QWidget *parent, PeerData *peer, const QString &title) : ScrolledWidget(parent) , _peer(peer) , _title(title) { } -void BlockWidget::resizeToWidth(int newWidth) { - resize(newWidth, resizeGetHeight(newWidth)); -} - int BlockWidget::contentTop() const { - return st::profileBlockMarginTop + st::profileBlockTitleHeight; + return st::profileBlockMarginTop + (emptyTitle() ? 0 : st::profileBlockTitleHeight); } void BlockWidget::paintEvent(QPaintEvent *e) { Painter p(this); + paintTitle(p); + paintContents(p); +} + +void BlockWidget::paintTitle(Painter &p) { + if (emptyTitle()) return; + p.setFont(st::profileBlockTitleFont); p.setPen(st::profileBlockTitleFg); int titleLeft = st::profileBlockTitlePosition.x(); int titleTop = st::profileBlockMarginTop + st::profileBlockTitlePosition.y(); p.drawTextLeft(titleLeft, titleTop, width(), _title); - - paintContents(p); } int defaultOutlineButtonLeft() { diff --git a/Telegram/SourceFiles/profile/profile_block_widget.h b/Telegram/SourceFiles/profile/profile_block_widget.h index da93aad75d..8f57e04e71 100644 --- a/Telegram/SourceFiles/profile/profile_block_widget.h +++ b/Telegram/SourceFiles/profile/profile_block_widget.h @@ -24,22 +24,12 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org namespace Profile { -class BlockWidget : public TWidget, public Notify::Observer { +class BlockWidget : public ScrolledWidget, public Notify::Observer { Q_OBJECT public: BlockWidget(QWidget *parent, PeerData *peer, const QString &title); - // Count new height for width=newWidth and resize to it. - void resizeToWidth(int newWidth); - - // Updates the area that is visible inside the scroll container. - virtual void setVisibleTopBottom(int visibleTop, int visibleBottom) { - } - - virtual ~BlockWidget() { - } - signals: void heightUpdated(); @@ -52,7 +42,7 @@ protected: int contentTop() const; // Resizes content and counts natural widget height for the desired width. - virtual int resizeGetHeight(int newWidth) = 0; + int resizeGetHeight(int newWidth) override = 0; void contentSizeUpdated() { resizeToWidth(width()); @@ -63,7 +53,13 @@ protected: return _peer; } + bool emptyTitle() const { + return _title.isEmpty(); + } + private: + void paintTitle(Painter &p); + PeerData *_peer; QString _title; diff --git a/Telegram/SourceFiles/profile/profile_members_widget.cpp b/Telegram/SourceFiles/profile/profile_members_widget.cpp index af1c9cfc05..d9b59f13f4 100644 --- a/Telegram/SourceFiles/profile/profile_members_widget.cpp +++ b/Telegram/SourceFiles/profile/profile_members_widget.cpp @@ -37,7 +37,8 @@ namespace Profile { using UpdateFlag = Notify::PeerUpdate::Flag; -MembersWidget::MembersWidget(QWidget *parent, PeerData *peer) : BlockWidget(parent, peer, lang(lng_profile_participants_section)) { +MembersWidget::MembersWidget(QWidget *parent, PeerData *peer, TitleVisibility titleVisibility) +: BlockWidget(parent, peer, (titleVisibility == TitleVisibility::Visible) ? lang(lng_profile_participants_section) : QString()) { setMouseTracking(true); _removeWidth = st::normalFont->width(lang(lng_profile_kick)); @@ -309,7 +310,7 @@ void MembersWidget::refreshLimitReached() { auto chat = peer()->asChat(); if (!chat) return; - bool limitReachedShown = (_list.size() >= Global::ChatSizeMax()) && chat->amCreator(); + bool limitReachedShown = (_list.size() >= Global::ChatSizeMax()) && chat->amCreator() && !emptyTitle(); if (limitReachedShown && !_limitReachedInfo) { _limitReachedInfo = new FlatLabel(this, st::profileLimitReachedLabel, st::profileLimitReachedStyle); QString title = textRichPrepare(lng_profile_migrate_reached(lt_count, Global::ChatSizeMax())); diff --git a/Telegram/SourceFiles/profile/profile_members_widget.h b/Telegram/SourceFiles/profile/profile_members_widget.h index 45f7178947..f71e7f21d2 100644 --- a/Telegram/SourceFiles/profile/profile_members_widget.h +++ b/Telegram/SourceFiles/profile/profile_members_widget.h @@ -38,7 +38,11 @@ class MembersWidget : public BlockWidget { Q_OBJECT public: - MembersWidget(QWidget *parent, PeerData *peer); + enum class TitleVisibility { + Visible, + Hidden, + }; + MembersWidget(QWidget *parent, PeerData *peer, TitleVisibility titleVisibility = TitleVisibility::Visible); void setVisibleTopBottom(int visibleTop, int visibleBottom) override; int onlineCount() const { @@ -57,11 +61,11 @@ protected: void mousePressEvent(QMouseEvent *e) override; void mouseReleaseEvent(QMouseEvent *e) override; void enterEvent(QEvent *e) override; - void enterFromChildEvent(QEvent *e) override { + void enterFromChildEvent(QEvent *e, QWidget *child) override { enterEvent(e); } void leaveEvent(QEvent *e) override; - void leaveToChildEvent(QEvent *e) override { + void leaveToChildEvent(QEvent *e, QWidget *child) override { leaveEvent(e); } diff --git a/Telegram/SourceFiles/ui/animation.h b/Telegram/SourceFiles/ui/animation.h index 90422651a2..ff1a944edf 100644 --- a/Telegram/SourceFiles/ui/animation.h +++ b/Telegram/SourceFiles/ui/animation.h @@ -449,13 +449,18 @@ private: } else { _data->a.update(dt, _data->transition); } - if (timer) { - _data->update.call(); - } + + Callback callbackCache, *toCall = &_data->update; if (!_data->_a.animating()) { + callbackCache = std_::move(_data->update); + toCall = &callbackCache; + delete _data; _data = nullptr; } + if (timer) { + toCall->call(); + } } }; diff --git a/Telegram/SourceFiles/ui/inner_dropdown.cpp b/Telegram/SourceFiles/ui/inner_dropdown.cpp new file mode 100644 index 0000000000..dd1b492832 --- /dev/null +++ b/Telegram/SourceFiles/ui/inner_dropdown.cpp @@ -0,0 +1,185 @@ +/* +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 +*/ +#include "stdafx.h" +#include "ui/inner_dropdown.h" + +#include "mainwindow.h" +#include "ui/scrollarea.h" +#include "profile/profile_members_widget.h" + +namespace Ui { + +InnerDropdown::InnerDropdown(QWidget *parent, const style::dropdown &st, const style::flatScroll &scrollSt) : TWidget(parent) +, _st(st) +, _shadow(_st.shadow) +, _scroll(this, scrollSt) { + _hideTimer.setSingleShot(true); + connect(&_hideTimer, SIGNAL(timeout()), this, SLOT(onHideStart())); + + connect(_scroll, SIGNAL(scrolled()), this, SLOT(onScroll())); + + if (cPlatform() == dbipMac || cPlatform() == dbipMacOld) { + connect(App::wnd()->windowHandle(), SIGNAL(activeChanged()), this, SLOT(onWindowActiveChanged())); + } + + hide(); +} + +void InnerDropdown::setOwnedWidget(ScrolledWidget *widget) { + _scroll->setOwnedWidget(widget); + widget->show(); + widget->move(0, 0); + widget->resizeToWidth(_scroll->width() - st::scrollDef.width); +} + +void InnerDropdown::onWindowActiveChanged() { + if (!App::wnd()->windowHandle()->isActive() && !isHidden()) { + leaveEvent(nullptr); + } +} + +void InnerDropdown::resizeEvent(QResizeEvent *e) { + _scroll->setGeometry(rect().marginsRemoved(_st.padding)); + if (auto widget = static_cast(_scroll->widget())) { + widget->resizeToWidth(_scroll->width() - st::scrollDef.width); + onScroll(); + } +} + +void InnerDropdown::onScroll() { + if (auto widget = static_cast(_scroll->widget())) { + int visibleTop = _scroll->scrollTop(); + int visibleBottom = visibleTop + _scroll->height(); + widget->setVisibleTopBottom(visibleTop, visibleBottom); + } +} + +void InnerDropdown::paintEvent(QPaintEvent *e) { + QPainter p(this); + + if (!_cache.isNull()) { + bool animating = _a_appearance.animating(getms()); + if (animating) { + p.setOpacity(_a_appearance.current(_hiding)); + } else if (_hiding) { + hidingFinished(); + return; + } + p.drawPixmap(0, 0, _cache); + if (!animating) { + showChildren(); + _cache = QPixmap(); + } + return; + } + + // draw shadow + QRect shadowedRect = rect().marginsRemoved(_st.padding); + _shadow.paint(p, shadowedRect, _st.shadowShift); + p.fillRect(shadowedRect, st::windowBg); +} + +void InnerDropdown::enterEvent(QEvent *e) { + _hideTimer.stop(); + if (_hiding) showingStarted(); + return TWidget::enterEvent(e); +} + +void InnerDropdown::leaveEvent(QEvent *e) { + if (_a_appearance.animating(getms())) { + onHideStart(); + } else { + _hideTimer.start(300); + } + return TWidget::leaveEvent(e); +} + +void InnerDropdown::otherEnter() { + _hideTimer.stop(); + showingStarted(); +} + +void InnerDropdown::otherLeave() { + if (_a_appearance.animating(getms())) { + onHideStart(); + } else { + _hideTimer.start(0); + } +} + +void InnerDropdown::onHideStart() { + if (_hiding) return; + + _hiding = true; + startAnimation(); +} + +void InnerDropdown::startAnimation() { + auto from = _hiding ? 1. : 0.; + auto to = _hiding ? 0. : 1.; + if (_a_appearance.isNull()) { + showChildren(); + _cache = myGrab(this); + } + hideChildren(); + START_ANIMATION(_a_appearance, func(this, &InnerDropdown::repaintCallback), from, to, _st.duration, anim::linear); +} + +void InnerDropdown::hidingFinished() { + hide(); +// showChildren(); + emit hidden(); +} + +void InnerDropdown::showingStarted() { + if (isHidden()) { + show(); + } else if (!_hiding) { + return; + } + _hiding = false; + startAnimation(); +} + +void InnerDropdown::repaintCallback() { + update(); + if (!_a_appearance.animating(getms()) && _hiding) { + _hiding = false; + hidingFinished(); + } +} + +bool InnerDropdown::eventFilter(QObject *obj, QEvent *e) { + if (e->type() == QEvent::Enter) { + otherEnter(); + } else if (e->type() == QEvent::Leave) { + otherLeave(); + } else if (e->type() == QEvent::MouseButtonPress && static_cast(e)->button() == Qt::LeftButton) { + if (isHidden() || _hiding) { + otherEnter(); + } else { + otherLeave(); + } + } + return false; +} + +} // namespace Ui diff --git a/Telegram/SourceFiles/ui/inner_dropdown.h b/Telegram/SourceFiles/ui/inner_dropdown.h new file mode 100644 index 0000000000..827016002d --- /dev/null +++ b/Telegram/SourceFiles/ui/inner_dropdown.h @@ -0,0 +1,82 @@ +/* +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 +*/ +#pragma once + +class ScrollArea; + +namespace Ui { + +class InnerDropdown : public TWidget { + Q_OBJECT + +public: + InnerDropdown(QWidget *parent, const style::dropdown &st = st::dropdownDef, const style::flatScroll &scrollSt = st::scrollDef); + + void setOwnedWidget(ScrolledWidget *widget); + + bool overlaps(const QRect &globalRect) { + if (isHidden() || !_a_appearance.isNull()) return false; + + return rect().marginsRemoved(_st.padding).contains(QRect(mapFromGlobal(globalRect.topLeft()), globalRect.size())); + } + + void otherEnter(); + void otherLeave(); + +protected: + void resizeEvent(QResizeEvent *e) override; + void paintEvent(QPaintEvent *e) override; + void enterEvent(QEvent *e) override; + void leaveEvent(QEvent *e) override; + + bool eventFilter(QObject *obj, QEvent *e) override; + +signals: + void hidden(); + +private slots: + void onHideStart(); + void onWindowActiveChanged(); + void onScroll(); + +private: + void repaintCallback(); + + void hidingFinished(); + void showingStarted(); + + void startAnimation(); + + const style::dropdown &_st; + + bool _hiding = false; + + QPixmap _cache; + FloatAnimation _a_appearance; + + QTimer _hideTimer; + + BoxShadow _shadow; + ChildWidget _scroll; + +}; + +} // namespace Ui diff --git a/Telegram/SourceFiles/ui/scrollarea.h b/Telegram/SourceFiles/ui/scrollarea.h index 1552cf5ade..43a0d4817d 100644 --- a/Telegram/SourceFiles/ui/scrollarea.h +++ b/Telegram/SourceFiles/ui/scrollarea.h @@ -268,3 +268,25 @@ public: } void paintEvent(QPaintEvent *e); }; + +class ScrolledWidget : public TWidget { +public: + ScrolledWidget(QWidget *parent = nullptr) : TWidget(parent) { + } + + // Count new height for width=newWidth and resize to it. + void resizeToWidth(int newWidth) { + resize(newWidth, resizeGetHeight(newWidth)); + } + + // Updates the area that is visible inside the scroll container. + virtual void setVisibleTopBottom(int visibleTop, int visibleBottom) { + } + +protected: + // Resizes content and counts natural widget height for the desired width. + virtual int resizeGetHeight(int newWidth) { + return height(); + } + +}; diff --git a/Telegram/SourceFiles/ui/twidget.h b/Telegram/SourceFiles/ui/twidget.h index 089b9379c3..2fe3a169e9 100644 --- a/Telegram/SourceFiles/ui/twidget.h +++ b/Telegram/SourceFiles/ui/twidget.h @@ -127,9 +127,9 @@ return qobject_cast(parentWidget()); \ const TWidget *tparent() const { \ return qobject_cast(parentWidget()); \ } \ -virtual void leaveToChildEvent(QEvent *e) { /* e -- from enterEvent() of child TWidget */ \ +virtual void leaveToChildEvent(QEvent *e, QWidget *child) { /* e -- from enterEvent() of child TWidget */ \ } \ -virtual void enterFromChildEvent(QEvent *e) { /* e -- from leaveEvent() of child TWidget */ \ +virtual void enterFromChildEvent(QEvent *e, QWidget *child) { /* e -- from leaveEvent() of child TWidget */ \ } \ void moveToLeft(int x, int y, int outerw = 0) { \ move(rtl() ? ((outerw > 0 ? outerw : parentWidget()->width()) - x - width()) : x, y); \ @@ -158,12 +158,12 @@ void rtlupdate(int x, int y, int w, int h) { \ protected: \ void enterEvent(QEvent *e) override { \ TWidget *p(tparent()); \ - if (p) p->leaveToChildEvent(e); \ + if (p) p->leaveToChildEvent(e, this); \ return enterEventHook(e); \ } \ void leaveEvent(QEvent *e) override { \ TWidget *p(tparent()); \ - if (p) p->enterFromChildEvent(e); \ + if (p) p->enterFromChildEvent(e, this); \ return leaveEventHook(e); \ } @@ -199,6 +199,9 @@ public: } } + virtual ~TWidget() { + } + protected: void enterEventHook(QEvent *e) { return QWidget::enterEvent(e); diff --git a/Telegram/SourceFiles/window/top_bar_widget.cpp b/Telegram/SourceFiles/window/top_bar_widget.cpp index 6c1772c790..2cb04a1696 100644 --- a/Telegram/SourceFiles/window/top_bar_widget.cpp +++ b/Telegram/SourceFiles/window/top_bar_widget.cpp @@ -86,9 +86,11 @@ void TopBarWidget::enterEvent(QEvent *e) { _a_appearance.start(); } -void TopBarWidget::enterFromChildEvent(QEvent *e) { - a_over.start(1); - _a_appearance.start(); +void TopBarWidget::enterFromChildEvent(QEvent *e, QWidget *child) { + if (child != _membersShowArea) { + a_over.start(1); + _a_appearance.start(); + } } void TopBarWidget::leaveEvent(QEvent *e) { @@ -96,9 +98,11 @@ void TopBarWidget::leaveEvent(QEvent *e) { _a_appearance.start(); } -void TopBarWidget::leaveToChildEvent(QEvent *e) { - a_over.start(0); - _a_appearance.start(); +void TopBarWidget::leaveToChildEvent(QEvent *e, QWidget *child) { + if (child != _membersShowArea) { + a_over.start(0); + _a_appearance.start(); + } } void TopBarWidget::step_appearance(float64 ms, bool timer) { @@ -112,6 +116,25 @@ void TopBarWidget::step_appearance(float64 ms, bool timer) { if (timer) update(); } +bool TopBarWidget::eventFilter(QObject *obj, QEvent *e) { + if (obj == _membersShowArea) { + switch (e->type()) { + case QEvent::MouseButtonPress: + mousePressEvent(static_cast(e)); + return true; + + case QEvent::Enter: + main()->setMembersShowAreaActive(true); + break; + + case QEvent::Leave: + main()->setMembersShowAreaActive(false); + break; + } + } + return TWidget::eventFilter(obj, e); +} + void TopBarWidget::paintEvent(QPaintEvent *e) { Painter p(this); @@ -136,8 +159,7 @@ void TopBarWidget::paintEvent(QPaintEvent *e) { } void TopBarWidget::mousePressEvent(QMouseEvent *e) { - PeerData *p = nullptr;// App::main() ? App::main()->profilePeer() : 0; - if (e->button() == Qt::LeftButton && e->pos().y() < st::topBarHeight && (p || !_selCount)) { + if (e->button() == Qt::LeftButton && e->pos().y() < st::topBarHeight && !_selCount) { emit clicked(); } } @@ -209,12 +231,16 @@ void TopBarWidget::startAnim() { _forward->hide(); _mediaType->hide(); _search->hide(); + if (_membersShowArea) { + _membersShowArea->hide(); + } _animating = true; } void TopBarWidget::stopAnim() { _animating = false; + updateMembersShowArea(); showAll(); } @@ -255,21 +281,53 @@ void TopBarWidget::showAll() { _search->hide(); _info->hide(); } + if (_membersShowArea) { + _membersShowArea->show(); + } resizeEvent(nullptr); } +void TopBarWidget::updateMembersShowArea() { + auto membersShowAreaNeeded = [this]() { + if (_selCount || App::main()->overviewPeer() || !_selPeer) { + return false; + } + if (_selPeer->isChat()) { + return true; + } + if (_selPeer->isMegagroup()) { + return (_selPeer->asMegagroup()->membersCount() < Global::ChatSizeMax()); + } + return false; + }; + if (!membersShowAreaNeeded()) { + if (_membersShowArea) { + main()->setMembersShowAreaActive(false); + _membersShowArea.destroy(); + } + return; + } else if (!_membersShowArea) { + _membersShowArea = new TWidget(this); + _membersShowArea->show(); + _membersShowArea->installEventFilter(this); + } + _membersShowArea->setGeometry(App::main()->getMembersShowAreaGeometry()); +} + void TopBarWidget::showSelected(uint32 selCount, bool canDelete) { - PeerData *p = nullptr;// App::main() ? App::main()->profilePeer() : 0; _selPeer = App::main()->overviewPeer() ? App::main()->overviewPeer() : App::main()->peer(); _selCount = selCount; _canDelete = canDelete; _selStr = (_selCount > 0) ? lng_selected_count(lt_count, _selCount) : QString(); _selStrWidth = st::btnDefLink.font->width(_selStr); - setCursor((!p && _selCount) ? style::cur_default : style::cur_pointer); + setCursor(_selCount ? style::cur_default : style::cur_pointer); + + updateMembersShowArea(); showAll(); } void TopBarWidget::updateAdaptiveLayout() { + updateMembersShowArea(); showAll(); } diff --git a/Telegram/SourceFiles/window/top_bar_widget.h b/Telegram/SourceFiles/window/top_bar_widget.h index 0f0d140bf8..696d1afd26 100644 --- a/Telegram/SourceFiles/window/top_bar_widget.h +++ b/Telegram/SourceFiles/window/top_bar_widget.h @@ -38,9 +38,9 @@ public: TopBarWidget(MainWidget *w); void enterEvent(QEvent *e) override; - void enterFromChildEvent(QEvent *e) override; + void enterFromChildEvent(QEvent *e, QWidget *child) override; void leaveEvent(QEvent *e) override; - void leaveToChildEvent(QEvent *e) override; + void leaveToChildEvent(QEvent *e, QWidget *child) override; void paintEvent(QPaintEvent *e) override; void mousePressEvent(QMouseEvent *e) override; void resizeEvent(QResizeEvent *e) override; @@ -54,8 +54,13 @@ public: void updateAdaptiveLayout(); + void updateMembersShowArea(); + Ui::RoundButton *mediaTypeButton(); +protected: + bool eventFilter(QObject *obj, QEvent *e) override; + public slots: void onForwardSelection(); void onDeleteSelection(); @@ -87,6 +92,7 @@ private: ChildWidget _mediaType; ChildWidget _search; + ChildWidget _membersShowArea = { nullptr }; }; diff --git a/Telegram/Telegram.vcxproj b/Telegram/Telegram.vcxproj index 764a5d330b..9e96d89113 100644 --- a/Telegram/Telegram.vcxproj +++ b/Telegram/Telegram.vcxproj @@ -310,6 +310,10 @@ true true + + true + true + true true @@ -631,6 +635,10 @@ true true + + true + true + true true @@ -988,6 +996,10 @@ true true + + true + true + true true @@ -1351,6 +1363,7 @@ + @@ -1949,6 +1962,20 @@ .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -DAL_LIBTYPE_STATIC -DUNICODE -DWIN32 -DWIN64 -DHAVE_STDINT_H -DZLIB_WINAPI -DQT_NO_DEBUG -DNDEBUG -D_SCL_SECURE_NO_WARNINGS "-I.\SourceFiles" "-I.\GeneratedFiles" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtCore\5.6.0\QtCore" "-I$(QTDIR)\include\QtGui\5.6.0\QtGui" "-I.\..\..\Libraries\breakpad\src" "-I.\..\..\Libraries\lzma\C" "-I.\..\..\Libraries\libexif-0.6.20" "-I.\..\..\Libraries\zlib-1.2.8" "-I.\..\..\Libraries\ffmpeg" "-I.\..\..\Libraries\openal-soft\include" "-I.\ThirdParty\minizip" "-I.\..\..\Libraries\openssl\Release\include" "-fstdafx.h" "-f../../SourceFiles/ui/twidget.h" + + $(QTDIR)\bin\moc.exe;%(FullPath) + Moc%27ing inner_dropdown.h... + .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" "-fstdafx.h" "-f../../SourceFiles/ui/inner_dropdown.h" -DAL_LIBTYPE_STATIC -DCUSTOM_API_ID -DUNICODE -DWIN32 -DWIN64 -DHAVE_STDINT_H -DZLIB_WINAPI -DQT_NO_DEBUG -DNDEBUG -D_SCL_SECURE_NO_WARNINGS "-I.\SourceFiles" "-I.\GeneratedFiles" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtCore\5.6.0\QtCore" "-I$(QTDIR)\include\QtGui\5.6.0\QtGui" "-I.\..\..\Libraries\breakpad\src" "-I.\..\..\Libraries\lzma\C" "-I.\..\..\Libraries\libexif-0.6.20" "-I.\..\..\Libraries\zlib-1.2.8" "-I.\..\..\Libraries\ffmpeg" "-I.\..\..\Libraries\openal-soft\include" "-I.\ThirdParty\minizip" "-I.\..\..\Libraries\openssl\Release\include" + $(QTDIR)\bin\moc.exe;%(FullPath) + Moc%27ing inner_dropdown.h... + .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" "-fstdafx.h" "-f../../SourceFiles/ui/inner_dropdown.h" -DAL_LIBTYPE_STATIC -DUNICODE -DWIN32 -DWIN64 -DHAVE_STDINT_H -DZLIB_WINAPI -D_SCL_SECURE_NO_WARNINGS "-I.\SourceFiles" "-I.\GeneratedFiles" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtCore\5.6.0\QtCore" "-I$(QTDIR)\include\QtGui\5.6.0\QtGui" "-I.\..\..\Libraries\breakpad\src" "-I.\..\..\Libraries\lzma\C" "-I.\..\..\Libraries\libexif-0.6.20" "-I.\..\..\Libraries\zlib-1.2.8" "-I.\..\..\Libraries\ffmpeg" "-I.\..\..\Libraries\openal-soft\include" "-I.\ThirdParty\minizip" "-I.\..\..\Libraries\openssl_debug\Debug\include" + $(QTDIR)\bin\moc.exe;%(FullPath) + Moc%27ing inner_dropdown.h... + .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" "-fstdafx.h" "-f../../SourceFiles/ui/inner_dropdown.h" -DAL_LIBTYPE_STATIC -DUNICODE -DWIN32 -DWIN64 -DHAVE_STDINT_H -DZLIB_WINAPI -DQT_NO_DEBUG -DNDEBUG -D_SCL_SECURE_NO_WARNINGS "-I.\SourceFiles" "-I.\GeneratedFiles" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtCore\5.6.0\QtCore" "-I$(QTDIR)\include\QtGui\5.6.0\QtGui" "-I.\..\..\Libraries\breakpad\src" "-I.\..\..\Libraries\lzma\C" "-I.\..\..\Libraries\libexif-0.6.20" "-I.\..\..\Libraries\zlib-1.2.8" "-I.\..\..\Libraries\ffmpeg" "-I.\..\..\Libraries\openal-soft\include" "-I.\ThirdParty\minizip" "-I.\..\..\Libraries\openssl\Release\include" + diff --git a/Telegram/Telegram.vcxproj.filters b/Telegram/Telegram.vcxproj.filters index ec92871bb9..131db69070 100644 --- a/Telegram/Telegram.vcxproj.filters +++ b/Telegram/Telegram.vcxproj.filters @@ -1329,6 +1329,18 @@ GeneratedFiles\Release + + SourceFiles\ui + + + GeneratedFiles\Deploy + + + GeneratedFiles\Debug + + + GeneratedFiles\Release + @@ -1864,6 +1876,9 @@ SourceFiles\platform\linux + + SourceFiles\ui +