diff --git a/Telegram/Resources/basic.style b/Telegram/Resources/basic.style index ab50abf44..77912a38d 100644 --- a/Telegram/Resources/basic.style +++ b/Telegram/Resources/basic.style @@ -1734,6 +1734,17 @@ dropdownDef: dropdown { width: 0px; } +defaultInnerDropdownShadow: icon { + { "dropdown_shadow", windowShadowFg }, +}; +defaultInnerDropdown: InnerDropdown { + padding: margins(10px, 10px, 10px, 10px); + shadow: defaultInnerDropdownShadow; + shadowShift: 1px; + + duration: 150; +} + dropdownAttachDocument: iconedButton(btnAttachDocument) { iconPos: point(14px, 13px); downIconPos: point(14px, 14px); diff --git a/Telegram/Resources/basic_types.style b/Telegram/Resources/basic_types.style index 30d53110a..c8e1fa0f4 100644 --- a/Telegram/Resources/basic_types.style +++ b/Telegram/Resources/basic_types.style @@ -246,6 +246,18 @@ dropdown { width: pixels; } +InnerDropdown { + padding: margins; + shadow: icon; + shadowShift: pixels; + + duration: int; + width: pixels; + + scrollMargin: margins; + scrollPadding: margins; +} + PopupMenu { skip: pixels; diff --git a/Telegram/Resources/icons/dropdown_shadow.png b/Telegram/Resources/icons/dropdown_shadow.png new file mode 100644 index 000000000..7055a8b49 Binary files /dev/null and b/Telegram/Resources/icons/dropdown_shadow.png differ diff --git a/Telegram/Resources/icons/dropdown_shadow@2x.png b/Telegram/Resources/icons/dropdown_shadow@2x.png new file mode 100644 index 000000000..1931c7b11 Binary files /dev/null and b/Telegram/Resources/icons/dropdown_shadow@2x.png differ diff --git a/Telegram/SourceFiles/codegen/style/generator.cpp b/Telegram/SourceFiles/codegen/style/generator.cpp index 801d5220f..84aa5e57e 100644 --- a/Telegram/SourceFiles/codegen/style/generator.cpp +++ b/Telegram/SourceFiles/codegen/style/generator.cpp @@ -646,6 +646,10 @@ bool Generator::collectUniqueValues() { int iconMaskIndex = 0; std::function collector = [this, &collector, &fontFamilyIndex, &iconMaskIndex](const Variable &variable) { auto value = variable.value; + if (!value.copyOf().isEmpty()) { + return true; + } + switch (value.type().tag) { case Tag::Invalid: case Tag::Int: diff --git a/Telegram/SourceFiles/codegen/style/module.cpp b/Telegram/SourceFiles/codegen/style/module.cpp index d11d4cdd0..ca49d8a23 100644 --- a/Telegram/SourceFiles/codegen/style/module.cpp +++ b/Telegram/SourceFiles/codegen/style/module.cpp @@ -52,7 +52,7 @@ const Struct *Module::findStruct(const FullName &name) const { return result; } for (const auto &module : included_) { - if (auto result = findStructInModule(name, *module)) { + if (auto result = module->findStruct(name)) { return result; } } @@ -68,12 +68,14 @@ bool Module::addVariable(const Variable &value) { return true; } -const Variable *Module::findVariable(const FullName &name) const { +const Variable *Module::findVariable(const FullName &name, bool *outFromThisModule) const { if (auto result = findVariableInModule(name, *this)) { + if (outFromThisModule) *outFromThisModule = true; return result; } for (const auto &module : included_) { - if (auto result = findVariableInModule(name, *module)) { + if (auto result = module->findVariable(name)) { + if (outFromThisModule) *outFromThisModule = false; return result; } } diff --git a/Telegram/SourceFiles/codegen/style/module.h b/Telegram/SourceFiles/codegen/style/module.h index a890fd7dd..f8a043286 100644 --- a/Telegram/SourceFiles/codegen/style/module.h +++ b/Telegram/SourceFiles/codegen/style/module.h @@ -75,7 +75,7 @@ public: // Returns false if there is a variable with such name already. bool addVariable(const Variable &value); // Returns nullptr if there is no such variable in result_ or any of included modules. - const Variable *findVariable(const FullName &name) const; + const Variable *findVariable(const FullName &name, bool *outFromThisModule = nullptr) const; bool hasVariables() const { return !variables_.isEmpty(); } diff --git a/Telegram/SourceFiles/codegen/style/parsed_file.cpp b/Telegram/SourceFiles/codegen/style/parsed_file.cpp index 859a7a428..a7acde231 100644 --- a/Telegram/SourceFiles/codegen/style/parsed_file.cpp +++ b/Telegram/SourceFiles/codegen/style/parsed_file.cpp @@ -43,6 +43,7 @@ constexpr int kErrorUnknownField = 803; constexpr int kErrorIdentifierNotFound = 804; constexpr int kErrorAlreadyDefined = 805; constexpr int kErrorBadString = 806; +constexpr int kErrorIconDuplicate = 807; QString findInputFile(const Options &options) { for (const auto &dir : options.includePaths) { @@ -351,7 +352,8 @@ structure::Value ParsedFile::defaultConstructedStruct(const structure::FullName } void ParsedFile::applyStructParent(structure::Value &result, const structure::FullName &parentName) { - if (auto parent = module_->findVariable(parentName)) { + bool fromTheSameModule = false; + if (auto parent = module_->findVariable(parentName, &fromTheSameModule)) { if (parent->value.type() != result.type()) { logErrorTypeMismatch() << "parent '" << logFullName(parentName) << "' has type '" << logType(parent->value.type()) << "' while child value has type " << logType(result.type()); return; @@ -374,6 +376,22 @@ void ParsedFile::applyStructParent(structure::Value &result, const structure::Fu const auto &srcValue(srcField.variable.value); auto &dstValue(dstField.variable.value); logAssert(srcValue.type() == dstValue.type()) << "struct field type check failed"; + + // Optimization: don't let the style files to contain unnamed inherited + // icons from the other (included) style files, because they will + // duplicate the binary data across different style c++ source files. + // + // Example: + // a.style has "A: Struct { icon: icon { ..file.. } };" and + // b.style has "B: Struct(A) { .. };" with non-overriden icon field. + // Then both style_a.cpp and style_b.cpp will contain binary data of "file". + if (!fromTheSameModule + && srcValue.type().tag == structure::TypeTag::Icon + && !srcValue.Icon().parts.empty() + && srcValue.copyOf().isEmpty()) { + logError(kErrorIconDuplicate) << "an unnamed icon field '" << logFullName(srcField.variable.name) << "' is inherited from parent '" << logFullName(parentName) << "'"; + return; + } dstValue = srcValue; dstField.status = Status::Implicit; } diff --git a/Telegram/SourceFiles/dropdown.cpp b/Telegram/SourceFiles/dropdown.cpp index 6d4ae62fc..d862a660d 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 ae7a33159..32faf3568 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/history/history.style b/Telegram/SourceFiles/history/history.style index 1a5d33f1d..9ab0bd79f 100644 --- a/Telegram/SourceFiles/history/history.style +++ b/Telegram/SourceFiles/history/history.style @@ -29,3 +29,16 @@ historyToDownArrow: icon { { "history_down_arrow", #b9b9b9, point(14px, 19px) }, }; historyToDownPaddingTop: 10px; + +membersInnerScroll: flatScroll(solidScroll) { + deltat: 3px; + deltab: 3px; + width: 8px; + deltax: 3px; +} +membersInnerWidth: 310px; +membersInnerHeightMax: 360px; +membersInnerDropdown: InnerDropdown(defaultInnerDropdown) { + scrollMargin: margins(0px, 5px, 0px, 5px); + scrollPadding: margins(0px, 3px, 8px, 3px); +} diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp index 9c8b6c3f9..cefc5bc07 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,40 @@ 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::membersInnerDropdown, st::membersInnerScroll); + _membersDropdown->setOwnedWidget(new Profile::MembersWidget(_membersDropdown, _peer, Profile::MembersWidget::TitleVisibility::Hidden)); + _membersDropdown->resize(st::membersInnerWidth, _membersDropdown->height()); + + _membersDropdown->setMaxHeight(countMembersDropdownHeightMax()); + _membersDropdown->moveToLeft(0, 0); + 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 +6110,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(); } } @@ -6687,6 +6724,9 @@ void HistoryWidget::resizeEvent(QResizeEvent *e) { _historyToEnd->moveToRight(st::historyToDownPosition.x(), _scroll.y() + _scroll.height() - _historyToEnd->height() - st::historyToDownPosition.y()); _emojiPan.setMaxHeight(height() - st::dropdownDef.padding.top() - st::dropdownDef.padding.bottom() - _attachEmoji.height()); + if (_membersDropdown) { + _membersDropdown->setMaxHeight(countMembersDropdownHeightMax()); + } switch (_attachDrag) { case DragStateFiles: @@ -7263,6 +7303,9 @@ bool HistoryWidget::pinnedMsgVisibilityUpdated() { connect(&_pinnedBar->cancel, SIGNAL(clicked()), this, SLOT(onPinnedHide())); _reportSpamPanel.raise(); _topShadow.raise(); + if (_membersDropdown) { + _membersDropdown->raise(); + } updatePinnedBar(); result = true; @@ -7645,6 +7688,13 @@ void HistoryWidget::cancelReplyAfterMediaSend(bool lastKeyboardUsed) { } } +int HistoryWidget::countMembersDropdownHeightMax() const { + int result = height() - st::membersInnerDropdown.padding.top() - st::membersInnerDropdown.padding.bottom(); + result -= _attachEmoji.height(); + accumulate_min(result, st::membersInnerHeightMax); + return result; +} + void HistoryWidget::cancelEdit() { if (!_editMsgId) return; diff --git a/Telegram/SourceFiles/historywidget.h b/Telegram/SourceFiles/historywidget.h index c1481437b..f506787c4 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(); @@ -863,6 +867,8 @@ private: void cancelReplyAfterMediaSend(bool lastKeyboardUsed); + int countMembersDropdownHeightMax() const; + MsgId _replyToId = 0; Text _replyToName; int _replyToNameVersion = 0; @@ -1099,6 +1105,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 8b48d365a..9439a61df 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 62a85bab9..7b57d4742 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 8045e7a22..ec34e421c 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 ba2c8ac5f..93007eefc 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 emptyTitle() ? 0 : (st::profileBlockMarginTop + 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 da93aad75..8f57e04e7 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 af1c9cfc0..d9b59f13f 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 45f717894..f71e7f21d 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 90422651a..ff1a944ed 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/boxshadow.cpp b/Telegram/SourceFiles/ui/boxshadow.cpp index 0ca3d787d..9e52f6229 100644 --- a/Telegram/SourceFiles/ui/boxshadow.cpp +++ b/Telegram/SourceFiles/ui/boxshadow.cpp @@ -60,7 +60,7 @@ BoxShadow::BoxShadow(const style::sprite &topLeft) : _size(topLeft.pxWidth()), _ _corners.setDevicePixelRatio(cRetinaFactor()); _colors.reserve(_pixsize); uchar prev = 0; - for (int32 i = 0; i < _pixsize; ++i) { + for (int i = 0; i < _pixsize; ++i) { uchar a = (cornersImage.pixel(QPoint(i, _pixsize - 1)) >> 24); if (a < prev) break; @@ -112,3 +112,79 @@ style::margins BoxShadow::getDimensions(int32 shifty) const { int32 d = _colors.size() / cIntRetinaFactor(); return style::margins(d - shifty, d - 2 * shifty, d - shifty, d); } + +namespace Ui { + +RectShadow::RectShadow(const style::icon &topLeft) : _size(topLeft.width()), _pixsize(_size * cIntRetinaFactor()) { + if (!_size) return; + + QImage cornersImage(_pixsize * 2, _pixsize * 2, QImage::Format_ARGB32_Premultiplied); + cornersImage.setDevicePixelRatio(cRetinaFactor()); + { + Painter p(&cornersImage); + p.setCompositionMode(QPainter::CompositionMode_Source); + topLeft.paint(p, QPoint(0, 0), _size); + } + if (rtl()) cornersImage = cornersImage.mirrored(true, false); + { + QPainter p(&cornersImage); + p.setCompositionMode(QPainter::CompositionMode_Source); + QImage m = cornersImage.mirrored(); + m.setDevicePixelRatio(cRetinaFactor()); + p.drawImage(0, _size, m, 0, _pixsize, _pixsize, _pixsize); + } + { + QPainter p(&cornersImage); + p.setCompositionMode(QPainter::CompositionMode_Source); + QImage m = cornersImage.mirrored(true, false); + m.setDevicePixelRatio(cRetinaFactor()); + p.drawImage(_size, 0, m, _pixsize, 0, _pixsize, _pixsize * 2); + } + _corners = QPixmap::fromImage(cornersImage, Qt::ColorOnly); + _corners.setDevicePixelRatio(cRetinaFactor()); + + uchar prev = 0; + for (int i = 0; i < _pixsize; ++i) { + uchar a = (cornersImage.pixel(QPoint(i, _pixsize - 1)) >> 24); + if (a < prev) break; + + ++_thickness; + prev = a; + } + + _left = QPixmap::fromImage(cornersImage.copy(0, _pixsize - 1, _thickness, 1), Qt::ColorOnly); + _left.setDevicePixelRatio(cRetinaFactor()); + _top = QPixmap::fromImage(cornersImage.copy(_pixsize - 1, 0, 1, _thickness), Qt::ColorOnly); + _top.setDevicePixelRatio(cRetinaFactor()); + _right = QPixmap::fromImage(cornersImage.copy(_pixsize * 2 - _thickness, _pixsize, _thickness, 1), Qt::ColorOnly); + _right.setDevicePixelRatio(cRetinaFactor()); + _bottom = QPixmap::fromImage(cornersImage.copy(_pixsize, _pixsize * 2 - _thickness, 1, _thickness), Qt::ColorOnly); + _bottom.setDevicePixelRatio(cRetinaFactor()); +} + +void RectShadow::paint(Painter &p, const QRect &box, int shifty, Sides sides) { + if (!_size) return; + + int32 rshifty = shifty * cIntRetinaFactor(); + int32 count = _thickness, countsize = count / cIntRetinaFactor(), minus = _size - countsize + shifty; + bool left = (sides & Side::Left), top = (sides & Side::Top), right = (sides & Side::Right), bottom = (sides & Side::Bottom); + if (left && top) p.drawPixmap(box.left() - _size + minus, box.top() - _size + minus + shifty, _corners, 0, 0, _pixsize, _pixsize); + if (right && top) p.drawPixmap(box.left() + box.width() - minus, box.top() - _size + minus + shifty, _corners, _pixsize, 0, _pixsize, _pixsize); + if (right && bottom) p.drawPixmap(box.left() + box.width() - minus, box.top() + box.height() - minus + shifty, _corners, _pixsize, _pixsize, _pixsize, _pixsize); + if (left && bottom) p.drawPixmap(box.left() - _size + minus, box.top() + box.height() - minus + shifty, _corners, 0, _pixsize, _pixsize, _pixsize); + + bool wasSmooth = p.renderHints().testFlag(QPainter::SmoothPixmapTransform); + if (wasSmooth) p.setRenderHint(QPainter::SmoothPixmapTransform, false); + if (left) p.drawPixmap(box.left() - countsize + shifty, box.top() + (top ? minus : 0) + shifty, countsize - shifty, box.height() - (bottom ? minus : 0) - (top ? minus : 0), _left, 0, 0, count - rshifty, 1); + if (top) p.drawPixmap(box.left() + (left ? minus : 0), box.top() - countsize + 2 * shifty, box.width() - (right ? minus : 0) - (left ? minus : 0), countsize - 2 * shifty, _top, 0, 0, 1, count - 2 * rshifty); + if (right) p.drawPixmap(box.left() + box.width(), box.top() + (top ? minus : 0) + shifty, countsize - shifty, box.height() - (bottom ? minus : 0) - (top ? minus : 0), _right, rshifty, 0, count - rshifty, 1); + if (bottom) p.drawPixmap(box.left() + (left ? minus : 0), box.top() + box.height(), box.width() - (right ? minus : 0) - (left ? minus : 0), countsize, _bottom, 0, 0, 1, count); + if (wasSmooth) p.setRenderHint(QPainter::SmoothPixmapTransform); +} + +style::margins RectShadow::getDimensions(int32 shifty) const { + int d = _thickness / cIntRetinaFactor(); + return style::margins(d - shifty, d - 2 * shifty, d - shifty, d); +} + +} // namespace Ui diff --git a/Telegram/SourceFiles/ui/boxshadow.h b/Telegram/SourceFiles/ui/boxshadow.h index bd4bb7765..3eeb30292 100644 --- a/Telegram/SourceFiles/ui/boxshadow.h +++ b/Telegram/SourceFiles/ui/boxshadow.h @@ -42,3 +42,32 @@ private: QVector _colors; }; + +namespace Ui { + +class RectShadow { +public: + enum class Side { + Left = 0x01, + Top = 0x02, + Right = 0x04, + Bottom = 0x08, + }; + Q_DECLARE_FLAGS(Sides, Side); + Q_DECLARE_FRIEND_OPERATORS_FOR_FLAGS(Sides); + + RectShadow(const style::icon &topLeft); + + void paint(Painter &p, const QRect &box, int shifty, Sides sides = Side::Left | Side::Top | Side::Right | Side::Bottom); + style::margins getDimensions(int shifty) const; + +private: + + int _size, _pixsize; + int _thickness = 0; + QPixmap _corners, _left, _top, _right, _bottom; + +}; +Q_DECLARE_OPERATORS_FOR_FLAGS(RectShadow::Sides); + +} // namespace Ui \ No newline at end of file diff --git a/Telegram/SourceFiles/ui/inner_dropdown.cpp b/Telegram/SourceFiles/ui/inner_dropdown.cpp new file mode 100644 index 000000000..ad6ccf238 --- /dev/null +++ b/Telegram/SourceFiles/ui/inner_dropdown.cpp @@ -0,0 +1,247 @@ +/* +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::InnerDropdown &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) { + auto container = new internal::Container(_scroll, widget, _st); + connect(container, SIGNAL(heightUpdated()), this, SLOT(onWidgetHeightUpdated())); + _scroll->setOwnedWidget(container); + container->resizeToWidth(_scroll->width()); + container->moveToLeft(0, 0); + container->show(); + widget->show(); +} + +void InnerDropdown::setMaxHeight(int newMaxHeight) { + _maxHeight = newMaxHeight; + updateHeight(); +} + +void InnerDropdown::onWidgetHeightUpdated() { + updateHeight(); +} + +void InnerDropdown::updateHeight() { + int newHeight = _st.padding.top() + _st.scrollMargin.top() + _st.scrollMargin.bottom() + _st.padding.bottom(); + if (auto widget = static_cast(_scroll->widget())) { + newHeight += widget->height(); + } + if (_maxHeight > 0) { + accumulate_min(newHeight, _maxHeight); + } + if (newHeight != height()) { + resize(width(), newHeight); + } +} + +void InnerDropdown::onWindowActiveChanged() { + if (!App::wnd()->windowHandle()->isActive() && !isHidden()) { + leaveEvent(nullptr); + } +} + +void InnerDropdown::resizeEvent(QResizeEvent *e) { + _scroll->setGeometry(rect().marginsRemoved(_st.padding).marginsRemoved(_st.scrollMargin)); + if (auto widget = static_cast(_scroll->widget())) { + widget->resizeToWidth(_scroll->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) { + Painter 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 internal { + +Container::Container(QWidget *parent, ScrolledWidget *child, const style::InnerDropdown &st) : ScrolledWidget(parent), _st(st) { + child->setParent(this); + child->moveToLeft(_st.scrollPadding.left(), _st.scrollPadding.top()); + connect(child, SIGNAL(heightUpdated()), this, SLOT(onHeightUpdate())); +} + +void Container::setVisibleTopBottom(int visibleTop, int visibleBottom) { + if (auto child = static_cast(children().front())) { + child->setVisibleTopBottom(visibleTop - _st.scrollPadding.top(), visibleBottom - _st.scrollPadding.top()); + } +} + +void Container::onHeightUpdate() { + int newHeight = _st.scrollPadding.top() + _st.scrollPadding.bottom(); + if (auto child = static_cast(children().front())) { + newHeight += child->height(); + } + if (newHeight != height()) { + resize(width(), newHeight); + emit heightUpdated(); + } +} + +int Container::resizeGetHeight(int newWidth) { + int innerWidth = newWidth - _st.scrollPadding.left() - _st.scrollPadding.right(); + int result = _st.scrollPadding.top() + _st.scrollPadding.bottom(); + if (auto child = static_cast(children().front())) { + child->resizeToWidth(innerWidth); + child->moveToLeft(_st.scrollPadding.left(), _st.scrollPadding.top()); + result += child->height(); + } + return result; +} + +} // namespace internal +} // namespace Ui diff --git a/Telegram/SourceFiles/ui/inner_dropdown.h b/Telegram/SourceFiles/ui/inner_dropdown.h new file mode 100644 index 000000000..55974f78e --- /dev/null +++ b/Telegram/SourceFiles/ui/inner_dropdown.h @@ -0,0 +1,112 @@ +/* +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 + +#include "ui/boxshadow.h" + +class ScrollArea; + +namespace Ui { + +class InnerDropdown : public TWidget { + Q_OBJECT + +public: + InnerDropdown(QWidget *parent, const style::InnerDropdown &st = st::defaultInnerDropdown, 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 setMaxHeight(int newMaxHeight); + + 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(); + void onWidgetHeightUpdated(); + +private: + void repaintCallback(); + + void hidingFinished(); + void showingStarted(); + + void startAnimation(); + + void updateHeight(); + + const style::InnerDropdown &_st; + + bool _hiding = false; + + QPixmap _cache; + FloatAnimation _a_appearance; + + QTimer _hideTimer; + + RectShadow _shadow; + ChildWidget _scroll; + + int _maxHeight = 0; + +}; + +namespace internal { + +class Container : public ScrolledWidget { + Q_OBJECT + +public: + Container(QWidget *parent, ScrolledWidget *child, const style::InnerDropdown &st); + void setVisibleTopBottom(int visibleTop, int visibleBottom) override; + +private slots: + void onHeightUpdate(); + +protected: + int resizeGetHeight(int newWidth) override; + +private: + const style::InnerDropdown &_st; + +}; + +} // namespace internal +} // namespace Ui diff --git a/Telegram/SourceFiles/ui/scrollarea.h b/Telegram/SourceFiles/ui/scrollarea.h index 1552cf5ad..524811308 100644 --- a/Telegram/SourceFiles/ui/scrollarea.h +++ b/Telegram/SourceFiles/ui/scrollarea.h @@ -268,3 +268,30 @@ public: } void paintEvent(QPaintEvent *e); }; + +class ScrolledWidget : public TWidget { + Q_OBJECT + +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(); + } + +signals: + void heightUpdated(); + +}; diff --git a/Telegram/SourceFiles/ui/twidget.h b/Telegram/SourceFiles/ui/twidget.h index 089b9379c..2fe3a169e 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 6c1772c79..2cb04a169 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 0f0d140bf..696d1afd2 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.pro b/Telegram/Telegram.pro index 321ae0d44..ad3dacc98 100644 --- a/Telegram/Telegram.pro +++ b/Telegram/Telegram.pro @@ -204,6 +204,7 @@ SOURCES += \ ./SourceFiles/ui/flatlabel.cpp \ ./SourceFiles/ui/flattextarea.cpp \ ./SourceFiles/ui/images.cpp \ + ./SourceFiles/ui/inner_dropdown.cpp \ ./SourceFiles/ui/scrollarea.cpp \ ./SourceFiles/ui/twidget.cpp \ ./SourceFiles/window/main_window.cpp \ @@ -361,6 +362,7 @@ HEADERS += \ ./SourceFiles/ui/flatlabel.h \ ./SourceFiles/ui/flattextarea.h \ ./SourceFiles/ui/images.h \ + ./SourceFiles/ui/inner_dropdown.h \ ./SourceFiles/ui/scrollarea.h \ ./SourceFiles/ui/twidget.h \ ./SourceFiles/window/main_window.h \ diff --git a/Telegram/Telegram.vcxproj b/Telegram/Telegram.vcxproj index 764a5d330..9e96d8911 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 ec92871bb..131db6907 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 + diff --git a/Telegram/Telegram.xcodeproj/qt_preprocess.mak b/Telegram/Telegram.xcodeproj/qt_preprocess.mak index 5d7ef7490..0707e159e 100644 --- a/Telegram/Telegram.xcodeproj/qt_preprocess.mak +++ b/Telegram/Telegram.xcodeproj/qt_preprocess.mak @@ -86,6 +86,7 @@ compilers: GeneratedFiles/qrc_telegram.cpp\ GeneratedFiles/Debug/moc_flattextarea.cpp\ GeneratedFiles/Debug/moc_history.cpp\ GeneratedFiles/Debug/moc_historywidget.cpp\ + GeneratedFiles/Debug/moc_inner_dropdown.cpp\ GeneratedFiles/Debug/moc_introcode.cpp\ GeneratedFiles/Debug/moc_introphone.cpp\ GeneratedFiles/Debug/moc_intropwdcheck.cpp\ @@ -223,6 +224,7 @@ compiler_moc_header_make_all: GeneratedFiles/Debug/moc_aboutbox.cpp\ GeneratedFiles/Debug/moc_flattextarea.cpp\ GeneratedFiles/Debug/moc_history.cpp\ GeneratedFiles/Debug/moc_historywidget.cpp\ + GeneratedFiles/Debug/moc_inner_dropdown.cpp\ GeneratedFiles/Debug/moc_introcode.cpp\ GeneratedFiles/Debug/moc_introphone.cpp\ GeneratedFiles/Debug/moc_intropwdcheck.cpp\ @@ -303,6 +305,7 @@ compiler_moc_header_clean: GeneratedFiles/Debug/moc_flattextarea.cpp\ GeneratedFiles/Debug/moc_history.cpp\ GeneratedFiles/Debug/moc_historywidget.cpp\ + GeneratedFiles/Debug/moc_inner_dropdown.cpp\ GeneratedFiles/Debug/moc_introcode.cpp\ GeneratedFiles/Debug/moc_introphone.cpp\ GeneratedFiles/Debug/moc_intropwdcheck.cpp\ @@ -456,6 +459,9 @@ GeneratedFiles/Debug/moc_history.cpp: SourceFiles/history.h GeneratedFiles/Debug/moc_historywidget.cpp: SourceFiles/historywidget.h $(MOC_FILE) SourceFiles/historywidget.h -o GeneratedFiles/Debug/moc_historywidget.cpp +GeneratedFiles/Debug/moc_inner_dropdown.cpp: SourceFiles/ui/inner_dropdown.h + $(MOC_FILE) SourceFiles/ui/inner_dropdown.h -o GeneratedFiles/Debug/moc_inner_dropdown.cpp + GeneratedFiles/Debug/moc_introcode.cpp: SourceFiles/intro/introcode.h $(MOC_FILE) SourceFiles/intro/introcode.h -o GeneratedFiles/Debug/moc_introcode.cpp