mirror of
https://github.com/vale981/tdesktop
synced 2025-03-06 10:11:41 -05:00
Add members list to info profile.
This commit is contained in:
parent
faeb1483f2
commit
f2a5862714
34 changed files with 943 additions and 300 deletions
|
@ -961,23 +961,28 @@ namespace {
|
|||
}
|
||||
|
||||
void feedChatAdmins(const MTPDupdateChatAdmins &d) {
|
||||
ChatData *chat = App::chat(d.vchat_id.v);
|
||||
auto chat = App::chat(d.vchat_id.v);
|
||||
if (chat->version <= d.vversion.v) {
|
||||
bool badVersion = (chat->version + 1 < d.vversion.v);
|
||||
if (badVersion) {
|
||||
chat->invalidateParticipants();
|
||||
Auth().api().requestPeer(chat);
|
||||
}
|
||||
auto wasCanEdit = chat->canEdit();
|
||||
auto badVersion = (chat->version + 1 < d.vversion.v);
|
||||
chat->version = d.vversion.v;
|
||||
if (mtpIsTrue(d.venabled)) {
|
||||
if (!badVersion) {
|
||||
chat->invalidateParticipants();
|
||||
}
|
||||
chat->flags |= MTPDchat::Flag::f_admins_enabled;
|
||||
} else {
|
||||
chat->flags &= ~MTPDchat::Flag::f_admins_enabled;
|
||||
}
|
||||
Notify::peerUpdatedDelayed(chat, Notify::PeerUpdate::Flag::AdminsChanged);
|
||||
if (badVersion || mtpIsTrue(d.venabled)) {
|
||||
chat->invalidateParticipants();
|
||||
Auth().api().requestPeer(chat);
|
||||
}
|
||||
if (wasCanEdit != chat->canEdit()) {
|
||||
Notify::peerUpdatedDelayed(
|
||||
chat,
|
||||
Notify::PeerUpdate::Flag::ChatCanEdit);
|
||||
}
|
||||
Notify::peerUpdatedDelayed(
|
||||
chat,
|
||||
Notify::PeerUpdate::Flag::AdminsChanged);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -90,27 +90,7 @@ membersInnerDropdown: InnerDropdown(defaultInnerDropdown) {
|
|||
scrollMargin: margins(0px, 5px, 0px, 5px);
|
||||
scrollPadding: margins(0px, 3px, 0px, 3px);
|
||||
}
|
||||
membersInnerItem: ProfilePeerListItem {
|
||||
left: 0px;
|
||||
bottom: 0px;
|
||||
button: OutlineButton {
|
||||
outlineWidth: 0px;
|
||||
|
||||
textBg: windowBg;
|
||||
textBgOver: windowBgOver;
|
||||
|
||||
textFg: windowSubTextFg;
|
||||
textFgOver: windowSubTextFgOver;
|
||||
|
||||
font: normalFont;
|
||||
padding: margins(11px, 5px, 11px, 5px);
|
||||
|
||||
ripple: defaultRippleAnimation;
|
||||
}
|
||||
statusFg: windowSubTextFg;
|
||||
statusFgOver: windowSubTextFgOver;
|
||||
statusFgActive: windowActiveTextFg;
|
||||
}
|
||||
membersInnerItem: defaultProfileMemberItem;
|
||||
|
||||
historyFileOutImage: icon {{ "history_file_image", historyFileOutIconFg }};
|
||||
historyFileOutImageSelected: icon {{ "history_file_image", historyFileOutIconFgSelected }};
|
||||
|
@ -456,14 +436,14 @@ historyAdminLogCancelSearch: CrossButton {
|
|||
height: 54px;
|
||||
|
||||
cross: CrossAnimation {
|
||||
size: 36px;
|
||||
size: 32px;
|
||||
skip: 10px;
|
||||
stroke: 2px;
|
||||
minScale: 0.3;
|
||||
}
|
||||
crossFg: menuIconFg;
|
||||
crossFgOver: menuIconFgOver;
|
||||
crossPosition: point(4px, 9px);
|
||||
crossPosition: point(6px, 11px);
|
||||
|
||||
duration: 150;
|
||||
loadingPeriod: 1000;
|
||||
|
|
|
@ -158,6 +158,7 @@ infoIconMediaPhoto: icon {{ "info_media_photo", infoIconFg }};
|
|||
infoInformationIconPosition: point(25px, 12px);
|
||||
infoNotificationsIconPosition: point(20px, 5px);
|
||||
infoSharedMediaIconPosition: point(20px, 24px);
|
||||
infoMembersIconPosition: point(20px, 15px);
|
||||
|
||||
infoLabeledOneLine: FlatLabel(defaultFlatLabel) {
|
||||
width: 0px; // No need to set minWidth in one-line text.
|
||||
|
@ -176,6 +177,16 @@ infoLabeled: FlatLabel(infoLabeledOneLine) {
|
|||
margin: margins(5px, 5px, 5px, 5px);
|
||||
}
|
||||
|
||||
infoBlockHeaderLabel: FlatLabel(infoProfileStatusLabel) {
|
||||
textFg: windowBoldFg;
|
||||
style: TextStyle(defaultTextStyle) {
|
||||
font: semiboldFont;
|
||||
linkFont: semiboldFont;
|
||||
linkFontOver: semiboldFont;
|
||||
}
|
||||
}
|
||||
infoBlockHeaderPosition: point(79px, 22px);
|
||||
|
||||
infoProfileToggle: Toggle(defaultToggle) {
|
||||
diameter: 16px;
|
||||
width: 14px;
|
||||
|
@ -209,14 +220,93 @@ infoMainButton: InfoProfileButton(infoProfileButton) {
|
|||
textFgOver: lightButtonFgOver;
|
||||
}
|
||||
infoSharedMediaCoverHeight: 62px;
|
||||
infoSharedMediaLabelPosition: point(79px, 22px);
|
||||
infoSharedMediaLabel: FlatLabel(infoProfileStatusLabel) {
|
||||
textFg: windowBoldFg;
|
||||
style: TextStyle(defaultTextStyle) {
|
||||
font: semiboldFont;
|
||||
linkFont: semiboldFont;
|
||||
linkFontOver: semiboldFont;
|
||||
}
|
||||
}
|
||||
infoSharedMediaButton: infoProfileButton;
|
||||
infoSharedMediaBottomSkip: 12px;
|
||||
|
||||
infoMembersHeader: 56px;
|
||||
infoMembersItem: ProfilePeerListItem(defaultProfileMemberItem) {
|
||||
photoPosition: point(18px, 6px);
|
||||
namePosition: point(79px, 11px);
|
||||
statusPosition: point(79px, 31px);
|
||||
}
|
||||
infoMembersButtonPosition: point(12px, 9px);
|
||||
infoMembersButtonIconPosition: point(6px, 6px);
|
||||
infoMembersButton: IconButton(defaultIconButton) {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
iconPosition: infoMembersButtonIconPosition;
|
||||
rippleAreaPosition: point(0px, 0px);
|
||||
rippleAreaSize: 44px;
|
||||
ripple: RippleAnimation(defaultRippleAnimation) {
|
||||
color: windowBgOver;
|
||||
}
|
||||
}
|
||||
infoMembersAddMember: IconButton(infoMembersButton) {
|
||||
icon: icon {{ "info_add_member", menuIconFg }};
|
||||
iconOver: icon {{ "info_add_member", menuIconFgOver }};
|
||||
}
|
||||
infoMembersSearch: IconButton(infoMembersButton) {
|
||||
icon: icon {{
|
||||
"top_bar_search",
|
||||
menuIconFg,
|
||||
infoMembersButtonIconPosition
|
||||
}};
|
||||
iconOver: icon {{
|
||||
"top_bar_search",
|
||||
menuIconFgOver,
|
||||
infoMembersButtonIconPosition
|
||||
}};
|
||||
iconPosition: point(0px, 0px);
|
||||
}
|
||||
infoMembersSearchActive: icon {
|
||||
{ size(44px, 44px), windowBg },
|
||||
{
|
||||
"top_bar_search",
|
||||
menuIconFgOver,
|
||||
infoMembersButtonIconPosition
|
||||
}
|
||||
};
|
||||
infoMembersSearchActiveLayer: icon {
|
||||
{ size(44px, 44px), boxBg },
|
||||
{
|
||||
"top_bar_search",
|
||||
menuIconFgOver,
|
||||
infoMembersButtonIconPosition
|
||||
}
|
||||
};
|
||||
infoMembersSearchField: FlatInput(defaultFlatInput) {
|
||||
textColor: windowFg;
|
||||
bgColor: topBarBg;
|
||||
bgActive: topBarBg;
|
||||
|
||||
font: font(fsize);
|
||||
|
||||
borderWidth: 0px;
|
||||
borderColor: topBarBg;
|
||||
borderActive: topBarBg;
|
||||
|
||||
width: 100px;
|
||||
height: 32px;
|
||||
textMrg: margins(0px, 0px, 0px, 0px);
|
||||
}
|
||||
infoMembersCancelSearch: CrossButton {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
|
||||
cross: CrossAnimation {
|
||||
size: 44px;
|
||||
skip: 16px;
|
||||
stroke: 2px;
|
||||
minScale: 0.3;
|
||||
}
|
||||
crossFg: menuIconFg;
|
||||
crossFgOver: menuIconFgOver;
|
||||
crossPosition: point(0px, 0px);
|
||||
|
||||
duration: 150;
|
||||
loadingPeriod: 1000;
|
||||
ripple: RippleAnimation(defaultRippleAnimation) {
|
||||
color: windowBgOver;
|
||||
}
|
||||
}
|
||||
infoMembersSearchTop: 15px;
|
||||
|
|
|
@ -23,6 +23,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
|||
#include "info/info_memento.h"
|
||||
#include "info/info_top_bar.h"
|
||||
#include "ui/rp_widget.h"
|
||||
#include "ui/focus_persister.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "window/section_widget.h"
|
||||
#include "window/window_controller.h"
|
||||
|
@ -106,6 +107,7 @@ void LayerWrap::parentResized() {
|
|||
auto parentSize = parentWidget()->size();
|
||||
auto parentWidth = parentSize.width();
|
||||
if (parentWidth < MinimalSupportedWidth()) {
|
||||
Ui::FocusPersister persister(this);
|
||||
auto localCopy = _controller;
|
||||
auto memento = MoveMemento(std::move(_content), Wrap::Narrow);
|
||||
localCopy->hideSpecialLayer(anim::type::instant);
|
||||
|
@ -124,6 +126,7 @@ void LayerWrap::parentResized() {
|
|||
}
|
||||
|
||||
bool LayerWrap::takeToThirdSection() {
|
||||
Ui::FocusPersister persister(this);
|
||||
auto localCopy = _controller;
|
||||
auto memento = MoveMemento(std::move(_content), Wrap::Side);
|
||||
localCopy->hideSpecialLayer(anim::type::instant);
|
||||
|
|
|
@ -21,6 +21,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
|||
#include "info/info_memento.h"
|
||||
|
||||
#include <rpl/never.h>
|
||||
#include <rpl/combine.h>
|
||||
#include "window/window_controller.h"
|
||||
#include "ui/widgets/scroll_area.h"
|
||||
#include "lang/lang_keys.h"
|
||||
|
@ -51,7 +52,7 @@ ContentWidget::ContentWidget(
|
|||
void ContentWidget::setWrap(Wrap wrap) {
|
||||
if (_wrap != wrap) {
|
||||
_wrap = wrap;
|
||||
wrapUpdatedHook();
|
||||
_wrapChanges.fire_copy(_wrap);
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
@ -62,7 +63,6 @@ void ContentWidget::resizeEvent(QResizeEvent *e) {
|
|||
QMargins(0, _scrollTopSkip, 0, 0));
|
||||
if (_scroll->geometry() != scrollGeometry) {
|
||||
_scroll->setGeometry(scrollGeometry);
|
||||
_inner->setMinimumHeight(_scroll->height());
|
||||
_inner->resizeToWidth(_scroll->width());
|
||||
}
|
||||
|
||||
|
@ -100,14 +100,18 @@ void ContentWidget::setGeometryWithTopMoved(
|
|||
Ui::RpWidget *ContentWidget::doSetInnerWidget(
|
||||
object_ptr<RpWidget> inner,
|
||||
int scrollTopSkip) {
|
||||
using namespace rpl::mappers;
|
||||
|
||||
_inner = _scroll->setOwnedWidget(std::move(inner));
|
||||
_inner->move(0, 0);
|
||||
|
||||
scrollTopValue()
|
||||
| rpl::start([this, inner = _inner](int value) {
|
||||
inner->setVisibleTopBottom(
|
||||
value,
|
||||
value + _scroll->height()); // TODO rpl::combine
|
||||
rpl::combine(
|
||||
_scroll->scrollTopValue(),
|
||||
_scroll->heightValue(),
|
||||
_inner->desiredHeightValue(),
|
||||
tuple($1, $1 + $2, $3))
|
||||
| rpl::start([inner = _inner](int top, int bottom, int desired) {
|
||||
inner->setVisibleTopBottom(top, bottom);
|
||||
}, _inner->lifetime());
|
||||
return _inner;
|
||||
}
|
||||
|
|
|
@ -121,8 +121,8 @@ protected:
|
|||
not_null<Window::Controller*> controller() const {
|
||||
return _controller;
|
||||
}
|
||||
Wrap wrap() const {
|
||||
return _wrap;
|
||||
rpl::producer<Wrap> wrapValue() const {
|
||||
return _wrapChanges.events_starting_with_copy(_wrap);
|
||||
}
|
||||
|
||||
void resizeEvent(QResizeEvent *e) override;
|
||||
|
@ -132,9 +132,6 @@ protected:
|
|||
int scrollTopSave() const;
|
||||
void scrollTopRestore(int scrollTop);
|
||||
|
||||
virtual void wrapUpdatedHook() {
|
||||
}
|
||||
|
||||
private:
|
||||
RpWidget *doSetInnerWidget(
|
||||
object_ptr<RpWidget> inner,
|
||||
|
@ -143,6 +140,7 @@ private:
|
|||
const not_null<Window::Controller*> _controller;
|
||||
const not_null<PeerData*> _peer;
|
||||
Wrap _wrap = Wrap::Layer;
|
||||
rpl::event_stream<Wrap> _wrapChanges;
|
||||
|
||||
int _scrollTopSkip = 0;
|
||||
object_ptr<Ui::ScrollArea> _scroll;
|
||||
|
|
|
@ -46,7 +46,7 @@ Button::Button(
|
|||
}
|
||||
|
||||
Button *Button::toggleOn(rpl::producer<bool> &&toggled) {
|
||||
_toggleOnLifetime.destroy();
|
||||
Expects(_toggle == nullptr);
|
||||
_toggle = std::make_unique<Ui::ToggleView>(
|
||||
isOver() ? _st.toggleOver : _st.toggle,
|
||||
false,
|
||||
|
@ -54,11 +54,11 @@ Button *Button::toggleOn(rpl::producer<bool> &&toggled) {
|
|||
clicks()
|
||||
| rpl::start([this](auto) {
|
||||
_toggle->setCheckedAnimated(!_toggle->checked());
|
||||
}, _toggleOnLifetime);
|
||||
}, lifetime());
|
||||
std::move(toggled)
|
||||
| rpl::start([this](bool toggled) {
|
||||
_toggle->setCheckedAnimated(toggled);
|
||||
}, _toggleOnLifetime);
|
||||
}, lifetime());
|
||||
_toggle->finishAnimation();
|
||||
return this;
|
||||
}
|
||||
|
|
|
@ -61,7 +61,6 @@ private:
|
|||
int _originalWidth = 0;
|
||||
int _textWidth = 0;
|
||||
std::unique_ptr<Ui::ToggleView> _toggle;
|
||||
rpl::lifetime _toggleOnLifetime;
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
|||
#include "info/profile/info_profile_cover.h"
|
||||
|
||||
#include <rpl/never.h>
|
||||
#include <rpl/combine.h>
|
||||
#include "info/profile/info_profile_values.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "styles/style_info.h"
|
||||
|
@ -36,6 +37,102 @@ namespace Info {
|
|||
namespace Profile {
|
||||
namespace {
|
||||
|
||||
class SectionToggle : public Ui::AbstractCheckView {
|
||||
public:
|
||||
SectionToggle(
|
||||
const style::InfoToggle &st,
|
||||
bool checked,
|
||||
base::lambda<void()> updateCallback);
|
||||
|
||||
QSize getSize() const override;
|
||||
void paint(
|
||||
Painter &p,
|
||||
int left,
|
||||
int top,
|
||||
int outerWidth,
|
||||
TimeMs ms) override;
|
||||
QImage prepareRippleMask() const override;
|
||||
bool checkRippleStartPosition(QPoint position) const override;
|
||||
|
||||
private:
|
||||
QSize rippleSize() const;
|
||||
|
||||
const style::InfoToggle &_st;
|
||||
|
||||
};
|
||||
|
||||
SectionToggle::SectionToggle(
|
||||
const style::InfoToggle &st,
|
||||
bool checked,
|
||||
base::lambda<void()> updateCallback)
|
||||
: AbstractCheckView(st.duration, checked, std::move(updateCallback))
|
||||
, _st(st) {
|
||||
}
|
||||
|
||||
QSize SectionToggle::getSize() const {
|
||||
return QSize(_st.size, _st.size);
|
||||
}
|
||||
|
||||
void SectionToggle::paint(
|
||||
Painter &p,
|
||||
int left,
|
||||
int top,
|
||||
int outerWidth,
|
||||
TimeMs ms) {
|
||||
auto sqrt2 = sqrt(2.);
|
||||
auto vLeft = rtlpoint(left + _st.skip, 0, outerWidth).x() + 0.;
|
||||
auto vTop = top + _st.skip + 0.;
|
||||
auto vWidth = _st.size - 2 * _st.skip;
|
||||
auto vHeight = _st.size - 2 * _st.skip;
|
||||
auto vStroke = _st.stroke / sqrt2;
|
||||
constexpr auto kPointCount = 6;
|
||||
std::array<QPointF, kPointCount> pathV = { {
|
||||
{ vLeft, vTop + (vHeight / 4.) + vStroke },
|
||||
{ vLeft + vStroke, vTop + (vHeight / 4.) },
|
||||
{ vLeft + (vWidth / 2.), vTop + (vHeight * 3. / 4.) - vStroke },
|
||||
{ vLeft + vWidth - vStroke, vTop + (vHeight / 4.) },
|
||||
{ vLeft + vWidth, vTop + (vHeight / 4.) + vStroke },
|
||||
{ vLeft + (vWidth / 2.), vTop + (vHeight * 3. / 4.) + vStroke },
|
||||
} };
|
||||
|
||||
auto toggled = currentAnimationValue(ms);
|
||||
auto alpha = (toggled - 1.) * M_PI_2;
|
||||
auto cosalpha = cos(alpha);
|
||||
auto sinalpha = sin(alpha);
|
||||
auto shiftx = vLeft + (vWidth / 2.);
|
||||
auto shifty = vTop + (vHeight / 2.);
|
||||
for (auto &point : pathV) {
|
||||
auto x = point.x() - shiftx;
|
||||
auto y = point.y() - shifty;
|
||||
point.setX(shiftx + x * cosalpha - y * sinalpha);
|
||||
point.setY(shifty + y * cosalpha + x * sinalpha);
|
||||
}
|
||||
QPainterPath path;
|
||||
path.moveTo(pathV[0]);
|
||||
for (int i = 1; i != kPointCount; ++i) {
|
||||
path.lineTo(pathV[i]);
|
||||
}
|
||||
path.lineTo(pathV[0]);
|
||||
|
||||
PainterHighQualityEnabler hq(p);
|
||||
p.fillPath(path, _st.color);
|
||||
}
|
||||
|
||||
QImage SectionToggle::prepareRippleMask() const {
|
||||
return Ui::RippleAnimation::ellipseMask(rippleSize());
|
||||
}
|
||||
|
||||
QSize SectionToggle::rippleSize() const {
|
||||
return getSize() + 2 * QSize(
|
||||
_st.rippleAreaPadding,
|
||||
_st.rippleAreaPadding);
|
||||
}
|
||||
|
||||
bool SectionToggle::checkRippleStartPosition(QPoint position) const {
|
||||
return QRect(QPoint(0, 0), rippleSize()).contains(position);
|
||||
|
||||
}
|
||||
|
||||
auto MembersStatusText(int count) {
|
||||
return lng_chat_status_members(lt_count, count);
|
||||
};
|
||||
|
@ -59,8 +156,55 @@ auto ChatStatusText(int fullCount, int onlineCount, bool isGroup) {
|
|||
|
||||
} // namespace
|
||||
|
||||
SectionWithToggle *SectionWithToggle::setToggleShown(
|
||||
rpl::producer<bool> &&shown) {
|
||||
_toggle.create(
|
||||
this,
|
||||
QString(),
|
||||
st::infoToggleCheckbox,
|
||||
std::make_unique<SectionToggle>(
|
||||
st::infoToggle,
|
||||
false,
|
||||
[this] { _toggle->updateCheck(); }));
|
||||
_toggle->hide();
|
||||
_toggle->lower();
|
||||
_toggle->setCheckAlignment(style::al_right);
|
||||
widthValue()
|
||||
| rpl::start([this](int newValue) {
|
||||
_toggle->setGeometry(0, 0, newValue, height());
|
||||
}, _toggle->lifetime());
|
||||
std::move(shown)
|
||||
| rpl::start([this](bool shown) {
|
||||
if (_toggle->isHidden() == shown) {
|
||||
_toggle->setVisible(shown);
|
||||
_toggleShown.fire_copy(shown);
|
||||
}
|
||||
}, lifetime());
|
||||
return this;
|
||||
}
|
||||
|
||||
rpl::producer<bool> SectionWithToggle::toggledValue() const {
|
||||
return _toggle
|
||||
? (rpl::single(_toggle->checked())
|
||||
| rpl::then(
|
||||
base::ObservableViewer(_toggle->checkedChanged)))
|
||||
: rpl::never<bool>();
|
||||
}
|
||||
|
||||
rpl::producer<bool> SectionWithToggle::toggleShownValue() const {
|
||||
return _toggleShown.events_starting_with(
|
||||
_toggle && !_toggle->isHidden());
|
||||
}
|
||||
|
||||
int SectionWithToggle::toggleSkip() const {
|
||||
return (!_toggle || _toggle->isHidden())
|
||||
? 0
|
||||
: st::infoToggleCheckbox.checkPosition.x()
|
||||
+ _toggle->checkRect().width();
|
||||
}
|
||||
|
||||
Cover::Cover(QWidget *parent, not_null<PeerData*> peer)
|
||||
: FixedHeightWidget(
|
||||
: SectionWithToggle(
|
||||
parent,
|
||||
st::infoProfilePhotoTop
|
||||
+ st::infoProfilePhotoSize
|
||||
|
@ -80,7 +224,11 @@ Cover::Cover(QWidget *parent, not_null<PeerData*> peer)
|
|||
}
|
||||
|
||||
void Cover::setupChildGeometry() {
|
||||
widthValue()
|
||||
using namespace rpl::mappers;
|
||||
rpl::combine(
|
||||
toggleShownValue(),
|
||||
widthValue(),
|
||||
$2)
|
||||
| rpl::start([this](int newWidth) {
|
||||
_userpic->moveToLeft(
|
||||
st::infoProfilePhotoLeft,
|
||||
|
@ -100,30 +248,6 @@ Cover *Cover::setOnlineCount(rpl::producer<int> &&count) {
|
|||
return this;
|
||||
}
|
||||
|
||||
Cover *Cover::setToggleShown(rpl::producer<bool> &&shown) {
|
||||
_toggle.create(
|
||||
this,
|
||||
QString(),
|
||||
st::infoToggleCheckbox,
|
||||
std::make_unique<SectionToggle>(
|
||||
st::infoToggle,
|
||||
false,
|
||||
[this] { _toggle->updateCheck(); }));
|
||||
_toggle->lower();
|
||||
_toggle->setCheckAlignment(style::al_right);
|
||||
widthValue()
|
||||
| rpl::start([this](int newValue) {
|
||||
_toggle->setGeometry(0, 0, newValue, height());
|
||||
}, _toggle->lifetime());
|
||||
std::move(shown)
|
||||
| rpl::start([this](bool shown) {
|
||||
if (_toggle->isHidden() == shown) {
|
||||
_toggle->setVisible(shown);
|
||||
}
|
||||
}, lifetime());
|
||||
return this;
|
||||
}
|
||||
|
||||
void Cover::initViewers() {
|
||||
using Flag = Notify::PeerUpdate::Flag;
|
||||
PeerUpdateValue(_peer, Flag::PhotoChanged)
|
||||
|
@ -201,11 +325,8 @@ void Cover::refreshStatusText() {
|
|||
void Cover::refreshNameGeometry(int newWidth) {
|
||||
auto nameWidth = newWidth
|
||||
- st::infoProfileNameLeft
|
||||
- st::infoProfileNameRight;
|
||||
if (_toggle) {
|
||||
nameWidth -= st::infoToggleCheckbox.checkPosition.x()
|
||||
+ _toggle->checkRect().width();
|
||||
}
|
||||
- st::infoProfileNameRight
|
||||
- toggleSkip();
|
||||
_name->resizeToWidth(nameWidth);
|
||||
_name->moveToLeft(
|
||||
st::infoProfileNameLeft,
|
||||
|
@ -216,11 +337,8 @@ void Cover::refreshNameGeometry(int newWidth) {
|
|||
void Cover::refreshStatusGeometry(int newWidth) {
|
||||
auto statusWidth = newWidth
|
||||
- st::infoProfileStatusLeft
|
||||
- st::infoProfileStatusRight;
|
||||
if (_toggle) {
|
||||
statusWidth -= st::infoToggleCheckbox.checkPosition.x()
|
||||
+ _toggle->checkRect().width();
|
||||
}
|
||||
- st::infoProfileStatusRight
|
||||
- toggleSkip();
|
||||
_status->resizeToWidth(statusWidth);
|
||||
_status->moveToLeft(
|
||||
st::infoProfileStatusLeft,
|
||||
|
@ -228,145 +346,38 @@ void Cover::refreshStatusGeometry(int newWidth) {
|
|||
newWidth);
|
||||
}
|
||||
|
||||
rpl::producer<bool> Cover::toggledValue() const {
|
||||
return _toggle
|
||||
? (rpl::single(_toggle->checked())
|
||||
| rpl::then(
|
||||
base::ObservableViewer(_toggle->checkedChanged)))
|
||||
: rpl::never<bool>();
|
||||
}
|
||||
|
||||
QMargins SharedMediaCover::getMargins() const {
|
||||
return QMargins(0, 0, 0, st::infoSharedMediaBottomSkip);
|
||||
}
|
||||
|
||||
SharedMediaCover::SharedMediaCover(QWidget *parent)
|
||||
: FixedHeightWidget(parent, st::infoSharedMediaCoverHeight) {
|
||||
: SectionWithToggle(parent, st::infoSharedMediaCoverHeight) {
|
||||
createLabel();
|
||||
}
|
||||
|
||||
void SharedMediaCover::createLabel() {
|
||||
using namespace rpl::mappers;
|
||||
auto label = object_ptr<Ui::FlatLabel>(
|
||||
this,
|
||||
Lang::Viewer(lng_profile_shared_media) | ToUpperValue(),
|
||||
st::infoSharedMediaLabel);
|
||||
st::infoBlockHeaderLabel);
|
||||
label->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
widthValue()
|
||||
| rpl::start([weak = label.data()](int newWidth) {
|
||||
weak->resizeToNaturalWidth(newWidth
|
||||
- st::infoSharedMediaLabelPosition.x()
|
||||
- st::infoSharedMediaButton.padding.right());
|
||||
rpl::combine(
|
||||
toggleShownValue(),
|
||||
widthValue(),
|
||||
$2)
|
||||
| rpl::start([this, weak = label.data()](int newWidth) {
|
||||
auto availableWidth = newWidth
|
||||
- st::infoBlockHeaderPosition.x()
|
||||
- st::infoSharedMediaButton.padding.right()
|
||||
- toggleSkip();
|
||||
weak->resizeToWidth(availableWidth);
|
||||
weak->moveToLeft(
|
||||
st::infoSharedMediaLabelPosition.x(),
|
||||
st::infoSharedMediaLabelPosition.y(),
|
||||
st::infoBlockHeaderPosition.x(),
|
||||
st::infoBlockHeaderPosition.y(),
|
||||
newWidth);
|
||||
}, label->lifetime());
|
||||
}
|
||||
|
||||
SharedMediaCover *SharedMediaCover::setToggleShown(
|
||||
rpl::producer<bool> &&shown) {
|
||||
_toggle.create(
|
||||
this,
|
||||
QString(),
|
||||
st::infoToggleCheckbox,
|
||||
std::make_unique<SectionToggle>(
|
||||
st::infoToggle,
|
||||
false,
|
||||
[this] { _toggle->updateCheck(); }));
|
||||
_toggle->lower();
|
||||
_toggle->setCheckAlignment(style::al_right);
|
||||
widthValue()
|
||||
| rpl::start([this](int newValue) {
|
||||
_toggle->setGeometry(0, 0, newValue, height());
|
||||
}, _toggle->lifetime());
|
||||
std::move(shown)
|
||||
| rpl::start([this](bool shown) {
|
||||
if (_toggle->isHidden() == shown) {
|
||||
_toggle->setVisible(shown);
|
||||
}
|
||||
}, lifetime());
|
||||
return this;
|
||||
}
|
||||
|
||||
rpl::producer<bool> SharedMediaCover::toggledValue() const {
|
||||
return _toggle
|
||||
? (rpl::single(_toggle->checked())
|
||||
| rpl::then(
|
||||
base::ObservableViewer(_toggle->checkedChanged)))
|
||||
: rpl::never<bool>();
|
||||
}
|
||||
|
||||
SectionToggle::SectionToggle(
|
||||
const style::InfoToggle &st,
|
||||
bool checked,
|
||||
base::lambda<void()> updateCallback)
|
||||
: AbstractCheckView(st.duration, checked, std::move(updateCallback))
|
||||
, _st(st) {
|
||||
}
|
||||
|
||||
QSize SectionToggle::getSize() const {
|
||||
return QSize(_st.size, _st.size);
|
||||
}
|
||||
|
||||
void SectionToggle::paint(
|
||||
Painter &p,
|
||||
int left,
|
||||
int top,
|
||||
int outerWidth,
|
||||
TimeMs ms) {
|
||||
auto sqrt2 = sqrt(2.);
|
||||
auto vLeft = rtlpoint(left + _st.skip, 0, outerWidth).x() + 0.;
|
||||
auto vTop = top + _st.skip + 0.;
|
||||
auto vWidth = _st.size - 2 * _st.skip;
|
||||
auto vHeight = _st.size - 2 * _st.skip;
|
||||
auto vStroke = _st.stroke / sqrt2;
|
||||
constexpr auto kPointCount = 6;
|
||||
std::array<QPointF, kPointCount> pathV = { {
|
||||
{ vLeft, vTop + (vHeight / 4.) + vStroke },
|
||||
{ vLeft + vStroke, vTop + (vHeight / 4.) },
|
||||
{ vLeft + (vWidth / 2.), vTop + (vHeight * 3. / 4.) - vStroke },
|
||||
{ vLeft + vWidth - vStroke, vTop + (vHeight / 4.) },
|
||||
{ vLeft + vWidth, vTop + (vHeight / 4.) + vStroke },
|
||||
{ vLeft + (vWidth / 2.), vTop + (vHeight * 3. / 4.) + vStroke },
|
||||
} };
|
||||
|
||||
auto toggled = currentAnimationValue(ms);
|
||||
auto alpha = (toggled - 1.) * M_PI_2;
|
||||
auto cosalpha = cos(alpha);
|
||||
auto sinalpha = sin(alpha);
|
||||
auto shiftx = vLeft + (vWidth / 2.);
|
||||
auto shifty = vTop + (vHeight / 2.);
|
||||
for (auto &point : pathV) {
|
||||
auto x = point.x() - shiftx;
|
||||
auto y = point.y() - shifty;
|
||||
point.setX(shiftx + x * cosalpha - y * sinalpha);
|
||||
point.setY(shifty + y * cosalpha + x * sinalpha);
|
||||
}
|
||||
QPainterPath path;
|
||||
path.moveTo(pathV[0]);
|
||||
for (int i = 1; i != kPointCount; ++i) {
|
||||
path.lineTo(pathV[i]);
|
||||
}
|
||||
path.lineTo(pathV[0]);
|
||||
|
||||
PainterHighQualityEnabler hq(p);
|
||||
p.fillPath(path, _st.color);
|
||||
}
|
||||
|
||||
QImage SectionToggle::prepareRippleMask() const {
|
||||
return Ui::RippleAnimation::ellipseMask(rippleSize());
|
||||
}
|
||||
|
||||
QSize SectionToggle::rippleSize() const {
|
||||
return getSize() + 2 * QSize(
|
||||
_st.rippleAreaPadding,
|
||||
_st.rippleAreaPadding);
|
||||
}
|
||||
|
||||
bool SectionToggle::checkRippleStartPosition(QPoint position) const {
|
||||
return QRect(QPoint(0, 0), rippleSize()).contains(position);
|
||||
|
||||
}
|
||||
|
||||
} // namespace Profile
|
||||
} // namespace Info
|
||||
|
|
|
@ -40,13 +40,33 @@ class SlideWrap;
|
|||
namespace Info {
|
||||
namespace Profile {
|
||||
|
||||
class Cover : public Ui::FixedHeightWidget {
|
||||
class SectionWithToggle : public Ui::FixedHeightWidget {
|
||||
public:
|
||||
using FixedHeightWidget::FixedHeightWidget;
|
||||
|
||||
SectionWithToggle *setToggleShown(rpl::producer<bool> &&shown);
|
||||
rpl::producer<bool> toggledValue() const;
|
||||
|
||||
protected:
|
||||
rpl::producer<bool> toggleShownValue() const;
|
||||
int toggleSkip() const;
|
||||
|
||||
private:
|
||||
object_ptr<Ui::Checkbox> _toggle = { nullptr };
|
||||
rpl::event_stream<bool> _toggleShown;
|
||||
|
||||
};
|
||||
|
||||
class Cover : public SectionWithToggle {
|
||||
public:
|
||||
Cover(QWidget *parent, not_null<PeerData*> peer);
|
||||
|
||||
Cover *setOnlineCount(rpl::producer<int> &&count);
|
||||
Cover *setToggleShown(rpl::producer<bool> &&shown);
|
||||
rpl::producer<bool> toggledValue() const;
|
||||
|
||||
Cover *setToggleShown(rpl::producer<bool> &&shown) {
|
||||
return static_cast<Cover*>(
|
||||
SectionWithToggle::setToggleShown(std::move(shown)));
|
||||
}
|
||||
|
||||
private:
|
||||
void setupChildGeometry();
|
||||
|
@ -64,49 +84,24 @@ private:
|
|||
object_ptr<::Profile::UserpicButton> _userpic;
|
||||
object_ptr<Ui::FlatLabel> _name = { nullptr };
|
||||
object_ptr<Ui::FlatLabel> _status = { nullptr };
|
||||
object_ptr<Ui::Checkbox> _toggle = { nullptr };
|
||||
//object_ptr<CoverDropArea> _dropArea = { nullptr };
|
||||
|
||||
};
|
||||
|
||||
class SharedMediaCover : public Ui::FixedHeightWidget {
|
||||
class SharedMediaCover : public SectionWithToggle {
|
||||
public:
|
||||
SharedMediaCover(QWidget *parent);
|
||||
|
||||
SharedMediaCover *setToggleShown(rpl::producer<bool> &&shown);
|
||||
rpl::producer<bool> toggledValue() const;
|
||||
SharedMediaCover *setToggleShown(rpl::producer<bool> &&shown) {
|
||||
return static_cast<SharedMediaCover*>(
|
||||
SectionWithToggle::setToggleShown(std::move(shown)));
|
||||
}
|
||||
|
||||
QMargins getMargins() const override;
|
||||
|
||||
private:
|
||||
void createLabel();
|
||||
|
||||
object_ptr<Ui::Checkbox> _toggle = { nullptr };
|
||||
|
||||
};
|
||||
|
||||
class SectionToggle : public Ui::AbstractCheckView {
|
||||
public:
|
||||
SectionToggle(
|
||||
const style::InfoToggle &st,
|
||||
bool checked,
|
||||
base::lambda<void()> updateCallback);
|
||||
|
||||
QSize getSize() const override;
|
||||
void paint(
|
||||
Painter &p,
|
||||
int left,
|
||||
int top,
|
||||
int outerWidth,
|
||||
TimeMs ms) override;
|
||||
QImage prepareRippleMask() const override;
|
||||
bool checkRippleStartPosition(QPoint position) const override;
|
||||
|
||||
private:
|
||||
QSize rippleSize() const;
|
||||
|
||||
const style::InfoToggle &_st;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Profile
|
||||
|
|
|
@ -27,6 +27,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
|||
#include "info/profile/info_profile_values.h"
|
||||
#include "info/profile/info_profile_cover.h"
|
||||
#include "info/profile/info_profile_icon.h"
|
||||
#include "info/profile/info_profile_members.h"
|
||||
#include "boxes/abstract_box.h"
|
||||
#include "boxes/add_contact_box.h"
|
||||
#include "mainwidget.h"
|
||||
|
@ -44,12 +45,13 @@ namespace Profile {
|
|||
|
||||
InnerWidget::InnerWidget(
|
||||
QWidget *parent,
|
||||
rpl::producer<Wrap> &&wrapValue,
|
||||
not_null<Window::Controller*> controller,
|
||||
not_null<PeerData*> peer)
|
||||
: RpWidget(parent)
|
||||
, _controller(controller)
|
||||
, _peer(peer)
|
||||
, _content(setupContent(this)) {
|
||||
, _content(setupContent(this, std::move(wrapValue))) {
|
||||
_content->heightValue()
|
||||
| rpl::start([this](int height) {
|
||||
TWidget::resizeToWidth(width());
|
||||
|
@ -67,7 +69,8 @@ rpl::producer<bool> InnerWidget::canHideDetails() const {
|
|||
}
|
||||
|
||||
object_ptr<Ui::RpWidget> InnerWidget::setupContent(
|
||||
RpWidget *parent) const {
|
||||
RpWidget *parent,
|
||||
rpl::producer<Wrap> &&wrapValue) const {
|
||||
auto result = object_ptr<Ui::VerticalLayout>(parent);
|
||||
auto cover = result->add(object_ptr<Cover>(
|
||||
result,
|
||||
|
@ -93,6 +96,12 @@ object_ptr<Ui::RpWidget> InnerWidget::setupContent(
|
|||
// setupChannelActions(result, channel);
|
||||
// }
|
||||
}
|
||||
if (_peer->isChat() || _peer->isMegagroup()) {
|
||||
result->add(object_ptr<Members>(
|
||||
result,
|
||||
std::move(wrapValue),
|
||||
_peer));
|
||||
}
|
||||
return std::move(result);
|
||||
}
|
||||
|
||||
|
@ -361,8 +370,7 @@ object_ptr<Ui::SlideWrap<>> InnerWidget::createSlideSkipWidget(
|
|||
void InnerWidget::visibleTopBottomUpdated(
|
||||
int visibleTop,
|
||||
int visibleBottom) {
|
||||
_visibleTop = visibleTop;
|
||||
_visibleBottom = visibleBottom;
|
||||
setChildVisibleTopBottom(_content, visibleTop, visibleBottom);
|
||||
}
|
||||
|
||||
void InnerWidget::saveState(not_null<Memento*> memento) {
|
||||
|
|
|
@ -34,6 +34,9 @@ class SlideWrap;
|
|||
} // namespace Ui
|
||||
|
||||
namespace Info {
|
||||
|
||||
enum class Wrap;
|
||||
|
||||
namespace Profile {
|
||||
|
||||
class Memento;
|
||||
|
@ -42,6 +45,7 @@ class InnerWidget final : public Ui::RpWidget {
|
|||
public:
|
||||
InnerWidget(
|
||||
QWidget *parent,
|
||||
rpl::producer<Wrap> &&wrapValue,
|
||||
not_null<Window::Controller*> controller,
|
||||
not_null<PeerData*> peer);
|
||||
|
||||
|
@ -64,7 +68,9 @@ protected:
|
|||
int visibleBottom) override;
|
||||
|
||||
private:
|
||||
object_ptr<RpWidget> setupContent(RpWidget *parent) const;
|
||||
object_ptr<RpWidget> setupContent(
|
||||
RpWidget *parent,
|
||||
rpl::producer<Wrap> &&wrapValue) const;
|
||||
object_ptr<RpWidget> setupDetails(RpWidget *parent) const;
|
||||
object_ptr<RpWidget> setupSharedMedia(RpWidget *parent) const;
|
||||
object_ptr<RpWidget> setupMuteToggle(RpWidget *parent) const;
|
||||
|
@ -86,8 +92,6 @@ private:
|
|||
not_null<Window::Controller*> _controller;
|
||||
not_null<PeerData*> _peer;
|
||||
|
||||
int _visibleTop = 0;
|
||||
int _visibleBottom = 0;
|
||||
int _minHeight = 0;
|
||||
|
||||
object_ptr<RpWidget> _content;
|
||||
|
|
281
Telegram/SourceFiles/info/profile/info_profile_members.cpp
Normal file
281
Telegram/SourceFiles/info/profile/info_profile_members.cpp
Normal file
|
@ -0,0 +1,281 @@
|
|||
/*
|
||||
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-2017 John Preston, https://desktop.telegram.org
|
||||
*/
|
||||
#include "info/profile/info_profile_members.h"
|
||||
|
||||
#include <rpl/combine.h>
|
||||
#include "info/profile/info_profile_values.h"
|
||||
#include "info/profile/info_profile_icon.h"
|
||||
#include "info/profile/info_profile_values.h"
|
||||
#include "info/info_memento.h"
|
||||
#include "profile/profile_block_group_members.h"
|
||||
#include "ui/widgets/labels.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/widgets/input_fields.h"
|
||||
#include "styles/style_info.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "boxes/confirm_box.h"
|
||||
#include "boxes/peer_list_controllers.h"
|
||||
|
||||
namespace Info {
|
||||
namespace Profile {
|
||||
namespace {
|
||||
|
||||
constexpr auto kEnableSearchMembersAfterCount = 50;
|
||||
|
||||
} // namespace
|
||||
|
||||
Members::Members(
|
||||
QWidget *parent,
|
||||
rpl::producer<Wrap> &&wrapValue,
|
||||
not_null<PeerData*> peer)
|
||||
: RpWidget(parent)
|
||||
, _peer(peer)
|
||||
, _labelWrap(this)
|
||||
, _label(setupHeader())
|
||||
, _addMember(this, st::infoMembersAddMember)
|
||||
, _searchField(
|
||||
this,
|
||||
st::infoMembersSearchField,
|
||||
langFactory(lng_participant_filter))
|
||||
, _search(this, st::infoMembersSearch)
|
||||
, _cancelSearch(this, st::infoMembersCancelSearch)
|
||||
, _list(setupList(this)) {
|
||||
setupButtons();
|
||||
std::move(wrapValue)
|
||||
| rpl::start([this](Wrap wrap) {
|
||||
_wrap = wrap;
|
||||
updateSearchOverrides();
|
||||
}, lifetime());
|
||||
}
|
||||
|
||||
object_ptr<Ui::FlatLabel> Members::setupHeader() {
|
||||
auto result = object_ptr<Ui::FlatLabel>(
|
||||
_labelWrap,
|
||||
MembersCountValue(_peer)
|
||||
| rpl::map([](int count) {
|
||||
return lng_chat_status_members(lt_count, count);
|
||||
})
|
||||
| ToUpperValue(),
|
||||
st::infoBlockHeaderLabel);
|
||||
result->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
return result;
|
||||
}
|
||||
|
||||
void Members::setupButtons() {
|
||||
using namespace rpl::mappers;
|
||||
|
||||
_searchField->hide();
|
||||
_cancelSearch->hideFast();
|
||||
|
||||
auto addMemberShown = CanAddMemberValue(_peer)
|
||||
| rpl::start_spawning(lifetime());
|
||||
widthValue()
|
||||
| rpl::start([button = _addMember.data()](int newWidth) {
|
||||
button->moveToRight(
|
||||
st::infoMembersButtonPosition.x(),
|
||||
st::infoMembersButtonPosition.y(),
|
||||
newWidth);
|
||||
}, _addMember->lifetime());
|
||||
_addMember->showOn(rpl::duplicate(addMemberShown));
|
||||
_addMember->clicks() // TODO throttle(ripple duration)
|
||||
| rpl::start([this](auto&&) {
|
||||
this->addMember();
|
||||
}, _addMember->lifetime());
|
||||
|
||||
auto searchShown = MembersCountValue(_peer)
|
||||
| rpl::map($1 >= kEnableSearchMembersAfterCount)
|
||||
| rpl::distinct_until_changed()
|
||||
| rpl::start_spawning(lifetime());
|
||||
_search->showOn(rpl::duplicate(searchShown));
|
||||
_search->clicks()
|
||||
| rpl::start([this](auto&&) {
|
||||
this->showSearch();
|
||||
}, _search->lifetime());
|
||||
_cancelSearch->clicks()
|
||||
| rpl::start([this](auto&&) {
|
||||
this->cancelSearch();
|
||||
}, _cancelSearch->lifetime());
|
||||
|
||||
rpl::combine(
|
||||
std::move(addMemberShown),
|
||||
std::move(searchShown))
|
||||
| rpl::start([this](auto&&) {
|
||||
this->resizeToWidth(width());
|
||||
}, lifetime());
|
||||
|
||||
object_ptr<FloatingIcon>(
|
||||
this,
|
||||
st::infoIconMembers,
|
||||
st::infoMembersIconPosition)->lower();
|
||||
}
|
||||
|
||||
object_ptr<Members::ListWidget> Members::setupList(
|
||||
RpWidget *parent) const {
|
||||
auto result = object_ptr<ListWidget>(
|
||||
parent,
|
||||
_peer,
|
||||
::Profile::GroupMembersWidget::TitleVisibility::Hidden,
|
||||
st::infoMembersItem);
|
||||
result->moveToLeft(0, st::infoMembersHeader);
|
||||
parent->widthValue()
|
||||
| rpl::start([list = result.data()](int newWidth) {
|
||||
list->resizeToWidth(newWidth);
|
||||
}, result->lifetime());
|
||||
result->heightValue()
|
||||
| rpl::start([parent](int listHeight) {
|
||||
auto newHeight = (listHeight > 0)
|
||||
? (st::infoMembersHeader + listHeight)
|
||||
: 0;
|
||||
parent->resize(parent->width(), newHeight);
|
||||
}, result->lifetime());
|
||||
return result;
|
||||
}
|
||||
|
||||
int Members::resizeGetHeight(int newWidth) {
|
||||
auto availableWidth = newWidth
|
||||
- st::infoMembersButtonPosition.x();
|
||||
if (!_addMember->isHidden()) {
|
||||
availableWidth -= st::infoMembersAddMember.width;
|
||||
}
|
||||
|
||||
auto cancelLeft = availableWidth - _cancelSearch->width();
|
||||
_cancelSearch->moveToLeft(
|
||||
cancelLeft,
|
||||
st::infoMembersButtonPosition.y());
|
||||
|
||||
auto searchShownLeft = st::infoMembersIconPosition.x()
|
||||
- st::infoMembersSearch.iconPosition.x();
|
||||
auto searchHiddenLeft = availableWidth - _search->width();
|
||||
auto searchShown = _searchShownAnimation.current(_searchShown ? 1. : 0.);
|
||||
auto searchCurrentLeft = anim::interpolate(
|
||||
searchHiddenLeft,
|
||||
searchShownLeft,
|
||||
searchShown);
|
||||
_search->moveToLeft(
|
||||
searchCurrentLeft,
|
||||
st::infoMembersButtonPosition.y());
|
||||
|
||||
auto fieldLeft = anim::interpolate(
|
||||
cancelLeft,
|
||||
st::infoBlockHeaderPosition.x(),
|
||||
searchShown);
|
||||
_searchField->setGeometryToLeft(
|
||||
fieldLeft,
|
||||
st::infoMembersSearchTop,
|
||||
cancelLeft - fieldLeft,
|
||||
_searchField->height());
|
||||
|
||||
_labelWrap->resize(
|
||||
searchCurrentLeft - st::infoBlockHeaderPosition.x(),
|
||||
_label->height());
|
||||
_labelWrap->moveToLeft(
|
||||
st::infoBlockHeaderPosition.x(),
|
||||
st::infoBlockHeaderPosition.y(),
|
||||
newWidth);
|
||||
|
||||
_label->resizeToWidth(searchHiddenLeft);
|
||||
_label->moveToLeft(0, 0);
|
||||
|
||||
return heightNoMargins();
|
||||
}
|
||||
|
||||
void Members::addMember() {
|
||||
if (auto chat = _peer->asChat()) {
|
||||
if (chat->count >= Global::ChatSizeMax() && chat->amCreator()) {
|
||||
Ui::show(Box<ConvertToSupergroupBox>(chat));
|
||||
} else {
|
||||
AddParticipantsBoxController::Start(chat);
|
||||
}
|
||||
} else if (auto channel = _peer->asChannel()) {
|
||||
if (channel->mgInfo) {
|
||||
auto &participants = channel->mgInfo->lastParticipants;
|
||||
AddParticipantsBoxController::Start(channel, { participants.cbegin(), participants.cend() });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Members::showSearch() {
|
||||
if (!_searchShown) {
|
||||
toggleSearch();
|
||||
}
|
||||
}
|
||||
|
||||
void Members::toggleSearch() {
|
||||
_searchShown = !_searchShown;
|
||||
_cancelSearch->toggleAnimated(_searchShown);
|
||||
_searchShownAnimation.start(
|
||||
[this] { searchAnimationCallback(); },
|
||||
_searchShown ? 0. : 1.,
|
||||
_searchShown ? 1. : 0.,
|
||||
st::slideWrapDuration);
|
||||
_search->setDisabled(_searchShown);
|
||||
if (_searchShown) {
|
||||
_searchField->show();
|
||||
_searchField->setFocus();
|
||||
} else {
|
||||
setFocus();
|
||||
}
|
||||
}
|
||||
|
||||
void Members::searchAnimationCallback() {
|
||||
if (!_searchShownAnimation.animating()) {
|
||||
_searchField->setVisible(_searchShown);
|
||||
updateSearchOverrides();
|
||||
_search->setPointerCursor(!_searchShown);
|
||||
}
|
||||
resizeToWidth(width());
|
||||
}
|
||||
|
||||
void Members::updateSearchOverrides() {
|
||||
auto iconOverride = !_searchShown
|
||||
? nullptr
|
||||
: (_wrap == Wrap::Layer)
|
||||
? &st::infoMembersSearchActiveLayer
|
||||
: &st::infoMembersSearchActive;
|
||||
_search->setIconOverride(iconOverride, iconOverride);
|
||||
}
|
||||
|
||||
void Members::cancelSearch() {
|
||||
if (_searchShown) {
|
||||
if (!_searchField->getLastText().isEmpty()) {
|
||||
_searchField->setText(QString());
|
||||
_searchField->updatePlaceholder();
|
||||
_searchField->setFocus();
|
||||
applySearch();
|
||||
} else {
|
||||
toggleSearch();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Members::applySearch() {
|
||||
|
||||
}
|
||||
|
||||
void Members::visibleTopBottomUpdated(
|
||||
int visibleTop,
|
||||
int visibleBottom) {
|
||||
setChildVisibleTopBottom(_list, visibleTop, visibleBottom);
|
||||
}
|
||||
|
||||
} // namespace Profile
|
||||
} // namespace Info
|
||||
|
89
Telegram/SourceFiles/info/profile/info_profile_members.h
Normal file
89
Telegram/SourceFiles/info/profile/info_profile_members.h
Normal file
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
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-2017 John Preston, https://desktop.telegram.org
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "ui/rp_widget.h"
|
||||
|
||||
namespace Ui {
|
||||
class FlatInput;
|
||||
class CrossButton;
|
||||
class IconButton;
|
||||
class FlatLabel;
|
||||
} // namespace Ui
|
||||
|
||||
namespace Profile {
|
||||
class GroupMembersWidget;
|
||||
} // namespace Profile
|
||||
|
||||
namespace Info {
|
||||
|
||||
enum class Wrap;
|
||||
|
||||
namespace Profile {
|
||||
|
||||
class Members : public Ui::RpWidget {
|
||||
public:
|
||||
Members(
|
||||
QWidget *parent,
|
||||
rpl::producer<Wrap> &&wrapValue,
|
||||
not_null<PeerData*> peer);
|
||||
|
||||
protected:
|
||||
void visibleTopBottomUpdated(
|
||||
int visibleTop,
|
||||
int visibleBottom) override;
|
||||
int resizeGetHeight(int newWidth) override;
|
||||
|
||||
private:
|
||||
using ListWidget = ::Profile::GroupMembersWidget;
|
||||
|
||||
object_ptr<Ui::FlatLabel> setupHeader();
|
||||
object_ptr<ListWidget> setupList(
|
||||
RpWidget *parent) const;
|
||||
|
||||
void setupButtons();
|
||||
void updateSearchOverrides();
|
||||
|
||||
void addMember();
|
||||
void showSearch();
|
||||
void toggleSearch();
|
||||
void cancelSearch();
|
||||
void applySearch();
|
||||
void searchAnimationCallback();
|
||||
|
||||
Wrap _wrap;
|
||||
not_null<PeerData*> _peer;
|
||||
object_ptr<Ui::RpWidget> _labelWrap;
|
||||
object_ptr<Ui::FlatLabel> _label;
|
||||
object_ptr<Ui::IconButton> _addMember;
|
||||
object_ptr<Ui::FlatInput> _searchField;
|
||||
object_ptr<Ui::IconButton> _search;
|
||||
object_ptr<Ui::CrossButton> _cancelSearch;
|
||||
object_ptr<ListWidget> _list;
|
||||
|
||||
Animation _searchShownAnimation;
|
||||
bool _searchShown = false;
|
||||
base::Timer _searchTimer;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Profile
|
||||
} // namespace Info
|
|
@ -220,6 +220,26 @@ rpl::producer<int> CommonGroupsCountValue(
|
|||
});
|
||||
}
|
||||
|
||||
rpl::producer<bool> CanAddMemberValue(
|
||||
not_null<PeerData*> peer) {
|
||||
if (auto chat = peer->asChat()) {
|
||||
return PeerUpdateValue(
|
||||
chat,
|
||||
Notify::PeerUpdate::Flag::ChatCanEdit)
|
||||
| rpl::map([chat](auto&&) {
|
||||
return chat->canEdit();
|
||||
});
|
||||
} else if (auto channel = peer->asChannel()) {
|
||||
return PeerUpdateValue(
|
||||
channel,
|
||||
Notify::PeerUpdate::Flag::ChannelRightsChanged)
|
||||
| rpl::map([channel](auto&&) {
|
||||
return channel->canAddMembers();
|
||||
});
|
||||
}
|
||||
return rpl::single(false);
|
||||
}
|
||||
|
||||
rpl::producer<bool> MultiLineTracker::atLeastOneShownValue() const {
|
||||
auto shown = std::vector<rpl::producer<bool>>();
|
||||
shown.reserve(_widgets.size());
|
||||
|
|
|
@ -74,6 +74,8 @@ rpl::producer<int> SharedMediaCountValue(
|
|||
Storage::SharedMediaType type);
|
||||
rpl::producer<int> CommonGroupsCountValue(
|
||||
not_null<UserData*> user);
|
||||
rpl::producer<bool> CanAddMemberValue(
|
||||
not_null<PeerData*> peer);
|
||||
|
||||
class MultiLineTracker {
|
||||
public:
|
||||
|
|
|
@ -48,6 +48,7 @@ Widget::Widget(
|
|||
: ContentWidget(parent, wrap, controller, peer) {
|
||||
_inner = setInnerWidget(object_ptr<InnerWidget>(
|
||||
this,
|
||||
wrapValue(),
|
||||
controller,
|
||||
peer));
|
||||
_inner->move(0, 0);
|
||||
|
|
|
@ -31,6 +31,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
|||
#include "window/top_bar_widget.h"
|
||||
#include "data/data_drafts.h"
|
||||
#include "ui/widgets/dropdown_menu.h"
|
||||
#include "ui/focus_persister.h"
|
||||
#include "chat_helpers/message_field.h"
|
||||
#include "chat_helpers/stickers.h"
|
||||
#include "observer_peer.h"
|
||||
|
@ -3028,6 +3029,7 @@ void MainWidget::checkMainSectionToLayer() {
|
|||
if (!_mainSection) {
|
||||
return;
|
||||
}
|
||||
Ui::FocusPersister persister(this);
|
||||
if (auto layer = _mainSection->moveContentToLayer(width())) {
|
||||
dropMainSection(_mainSection);
|
||||
_controller->showSpecialLayer(
|
||||
|
|
|
@ -121,21 +121,15 @@ profileInviteLinkText: FlatLabel(profileBlockTextPart) {
|
|||
|
||||
profileLimitReachedSkip: 6px;
|
||||
|
||||
profileMemberItem: ProfilePeerListItem {
|
||||
profileMemberItem: ProfilePeerListItem(defaultProfileMemberItem) {
|
||||
left: 8px;
|
||||
bottom: profileBlockMarginBottom;
|
||||
button: defaultLeftOutlineButton;
|
||||
statusFg: windowSubTextFg;
|
||||
maximalWidth: profileBlockWideWidthMax;
|
||||
statusFgOver: profileStatusFgOver;
|
||||
statusFgActive: windowActiveTextFg;
|
||||
}
|
||||
profileMemberHeight: 58px;
|
||||
profileMemberPaddingLeft: 16px;
|
||||
profileMemberPhotoSize: 46px;
|
||||
profileMemberPhotoPosition: point(12px, 6px);
|
||||
profileMemberNamePosition: point(68px, 11px);
|
||||
profileMemberNameFg: windowBoldFg;
|
||||
profileMemberStatusPosition: point(68px, 31px);
|
||||
profileMemberCreatorIcon: icon {{ "profile_admin_star", profileAdminStartFg, point(4px, 3px) }};
|
||||
profileMemberCreatorIconOver: icon {{ "profile_admin_star", profileAdminStarFgOver, point(4px, 3px) }};
|
||||
profileMemberAdminIcon: icon {{ "profile_admin_star", profileOtherAdminStarFg, point(4px, 3px) }};
|
||||
|
|
|
@ -35,7 +35,11 @@ namespace Profile {
|
|||
|
||||
using UpdateFlag = Notify::PeerUpdate::Flag;
|
||||
|
||||
GroupMembersWidget::GroupMembersWidget(QWidget *parent, PeerData *peer, TitleVisibility titleVisibility, const style::ProfilePeerListItem &st)
|
||||
GroupMembersWidget::GroupMembersWidget(
|
||||
QWidget *parent,
|
||||
PeerData *peer,
|
||||
TitleVisibility titleVisibility,
|
||||
const style::ProfilePeerListItem &st)
|
||||
: PeerListWidget(parent
|
||||
, peer
|
||||
, (titleVisibility == TitleVisibility::Visible) ? lang(lng_profile_participants_section) : QString()
|
||||
|
|
|
@ -45,7 +45,7 @@ PeerListWidget::PeerListWidget(QWidget *parent, PeerData *peer, const QString &t
|
|||
int PeerListWidget::resizeGetHeight(int newWidth) {
|
||||
auto newHeight = getListTop();
|
||||
|
||||
newHeight += _items.size() * st::profileMemberHeight;
|
||||
newHeight += _items.size() * _st.height;
|
||||
|
||||
return newHeight + _st.bottom;
|
||||
}
|
||||
|
@ -67,10 +67,10 @@ void PeerListWidget::paintContents(Painter &p) {
|
|||
auto top = getListTop();
|
||||
auto memberRowWidth = rowWidth();
|
||||
|
||||
auto from = floorclamp(_visibleTop - top, st::profileMemberHeight, 0, _items.size());
|
||||
auto to = ceilclamp(_visibleBottom - top, st::profileMemberHeight, 0, _items.size());
|
||||
auto from = floorclamp(_visibleTop - top, _st.height, 0, _items.size());
|
||||
auto to = ceilclamp(_visibleBottom - top, _st.height, 0, _items.size());
|
||||
for (auto i = from; i < to; ++i) {
|
||||
auto y = top + i * st::profileMemberHeight;
|
||||
auto y = top + i * _st.height;
|
||||
auto selected = (_menuRowIndex >= 0) ? (i == _menuRowIndex) : (_pressed >= 0) ? (i == _pressed) : (i == _selected);
|
||||
auto selectedRemove = selected && _selectedRemove;
|
||||
if (_pressed >= 0 && !_pressedRemove) {
|
||||
|
@ -87,7 +87,7 @@ void PeerListWidget::paintItem(Painter &p, int x, int y, Item *item, bool select
|
|||
|
||||
auto memberRowWidth = rowWidth();
|
||||
if (selected) {
|
||||
paintOutlinedRect(p, x, y, memberRowWidth, st::profileMemberHeight);
|
||||
paintOutlinedRect(p, x, y, memberRowWidth, _st.height);
|
||||
}
|
||||
if (auto &ripple = item->ripple) {
|
||||
ripple->paint(p, x + _st.button.outlineWidth, y, width(), ms);
|
||||
|
@ -95,16 +95,16 @@ void PeerListWidget::paintItem(Painter &p, int x, int y, Item *item, bool select
|
|||
ripple.reset();
|
||||
}
|
||||
}
|
||||
int skip = st::profileMemberPhotoPosition.x();
|
||||
int skip = _st.photoPosition.x();
|
||||
|
||||
item->peer->paintUserpicLeft(p, x + st::profileMemberPhotoPosition.x(), y + st::profileMemberPhotoPosition.y(), width(), st::profileMemberPhotoSize);
|
||||
item->peer->paintUserpicLeft(p, x + _st.photoPosition.x(), y + _st.photoPosition.y(), width(), _st.photoSize);
|
||||
|
||||
if (item->name.isEmpty()) {
|
||||
item->name.setText(st::msgNameStyle, App::peerName(item->peer), _textNameOptions);
|
||||
}
|
||||
int nameLeft = x + st::profileMemberNamePosition.x();
|
||||
int nameTop = y + st::profileMemberNamePosition.y();
|
||||
int nameWidth = memberRowWidth - st::profileMemberNamePosition.x() - skip;
|
||||
int nameLeft = x + _st.namePosition.x();
|
||||
int nameTop = y + _st.namePosition.y();
|
||||
int nameWidth = memberRowWidth - _st.namePosition.x() - skip;
|
||||
if (item->hasRemoveLink && selected) {
|
||||
p.setFont(selectedKick ? st::normalFont->underline() : st::normalFont);
|
||||
p.setPen(st::windowActiveTextFg);
|
||||
|
@ -128,7 +128,7 @@ void PeerListWidget::paintItem(Painter &p, int x, int y, Item *item, bool select
|
|||
p.setPen(selected ? _st.statusFgOver : _st.statusFg);
|
||||
}
|
||||
p.setFont(st::normalFont);
|
||||
p.drawTextLeft(x + st::profileMemberStatusPosition.x(), y + st::profileMemberStatusPosition.y(), width(), item->statusText);
|
||||
p.drawTextLeft(x + _st.statusPosition.x(), y + _st.statusPosition.y(), width(), item->statusText);
|
||||
}
|
||||
|
||||
void PeerListWidget::paintOutlinedRect(Painter &p, int x, int y, int w, int h) const {
|
||||
|
@ -155,13 +155,13 @@ void PeerListWidget::mousePressEvent(QMouseEvent *e) {
|
|||
auto item = _items[_pressed];
|
||||
if (!item->ripple) {
|
||||
auto memberRowWidth = rowWidth();
|
||||
auto mask = Ui::RippleAnimation::rectMask(QSize(memberRowWidth - _st.button.outlineWidth, st::profileMemberHeight));
|
||||
auto mask = Ui::RippleAnimation::rectMask(QSize(memberRowWidth - _st.button.outlineWidth, _st.height));
|
||||
item->ripple = std::make_unique<Ui::RippleAnimation>(_st.button.ripple, std::move(mask), [this, index = _pressed] {
|
||||
repaintRow(index);
|
||||
});
|
||||
}
|
||||
auto left = getListLeft() + _st.button.outlineWidth;
|
||||
auto top = getListTop() + st::profileMemberHeight * _pressed;
|
||||
auto top = getListTop() + _st.height * _pressed;
|
||||
item->ripple->add(e->pos() - QPoint(left, top));
|
||||
}
|
||||
}
|
||||
|
@ -250,14 +250,14 @@ void PeerListWidget::updateSelection() {
|
|||
auto top = getListTop();
|
||||
auto memberRowWidth = rowWidth();
|
||||
if (mouse.x() >= left && mouse.x() < left + memberRowWidth && mouse.y() >= top) {
|
||||
selected = (mouse.y() - top) / st::profileMemberHeight;
|
||||
selected = (mouse.y() - top) / _st.height;
|
||||
if (selected >= _items.size()) {
|
||||
selected = -1;
|
||||
} else if (_items[selected]->hasRemoveLink) {
|
||||
int skip = st::profileMemberPhotoPosition.x();
|
||||
int nameLeft = left + st::profileMemberNamePosition.x();
|
||||
int nameTop = top + _selected * st::profileMemberHeight + st::profileMemberNamePosition.y();
|
||||
int nameWidth = memberRowWidth - st::profileMemberNamePosition.x() - skip;
|
||||
int skip = _st.photoPosition.x();
|
||||
int nameLeft = left + _st.namePosition.x();
|
||||
int nameTop = top + _selected * _st.height + _st.namePosition.y();
|
||||
int nameWidth = memberRowWidth - _st.namePosition.x() - skip;
|
||||
if (mouse.x() >= nameLeft + nameWidth - _removeWidth && mouse.x() < nameLeft + nameWidth) {
|
||||
if (mouse.y() >= nameTop && mouse.y() < nameTop + st::normalFont->height) {
|
||||
selectedKick = true;
|
||||
|
@ -294,7 +294,7 @@ void PeerListWidget::repaintSelectedRow() {
|
|||
void PeerListWidget::repaintRow(int index) {
|
||||
if (index >= 0) {
|
||||
auto left = getListLeft();
|
||||
rtlupdate(left, getListTop() + index * st::profileMemberHeight, width() - left, st::profileMemberHeight);
|
||||
rtlupdate(left, getListTop() + index * _st.height, width() - left, _st.height);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -303,14 +303,16 @@ int PeerListWidget::getListLeft() const {
|
|||
}
|
||||
|
||||
int PeerListWidget::rowWidth() const {
|
||||
return qMin(width() - getListLeft(), st::profileBlockWideWidthMax);
|
||||
return _st.maximalWidth
|
||||
? qMin(width() - getListLeft(), _st.maximalWidth)
|
||||
: width() - getListLeft();
|
||||
}
|
||||
|
||||
void PeerListWidget::preloadPhotos() {
|
||||
int top = getListTop();
|
||||
int preloadFor = (_visibleBottom - _visibleTop) * PreloadHeightsCount;
|
||||
int from = floorclamp(_visibleTop - top, st::profileMemberHeight, 0, _items.size());
|
||||
int to = ceilclamp(_visibleBottom + preloadFor - top, st::profileMemberHeight, 0, _items.size());
|
||||
int from = floorclamp(_visibleTop - top, _st.height, 0, _items.size());
|
||||
int to = ceilclamp(_visibleBottom + preloadFor - top, _st.height, 0, _items.size());
|
||||
for (int i = from; i < to; ++i) {
|
||||
_items[i]->peer->loadUserpic();
|
||||
}
|
||||
|
|
|
@ -25,7 +25,10 @@ Copyright (c) 2014-2017 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) : RpWidget(parent)
|
||||
, _peer(peer)
|
||||
, _title(title) {
|
||||
}
|
||||
|
|
|
@ -21,12 +21,13 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
|||
#pragma once
|
||||
|
||||
#include "base/observer.h"
|
||||
#include "ui/rp_widget.h"
|
||||
|
||||
namespace Profile {
|
||||
|
||||
class SectionMemento;
|
||||
|
||||
class BlockWidget : public TWidget, protected base::Subscriber {
|
||||
class BlockWidget : public Ui::RpWidget, protected base::Subscriber {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
|
|
@ -432,6 +432,45 @@ inline auto operator~(Type &&value) {
|
|||
std::forward<Type>(value));
|
||||
}
|
||||
|
||||
template <typename ...Mappers>
|
||||
class tuple_mapper {
|
||||
template <typename ...Args>
|
||||
using tuple_result = std::tuple<decltype(
|
||||
std::declval<wrap_mapper_t<std::decay_t<Mappers>>>()(
|
||||
std::declval<Args>()...))...>;
|
||||
public:
|
||||
template <typename ...OtherMappers>
|
||||
tuple_mapper(OtherMappers &&...mappers) : _mappers(
|
||||
std::forward<OtherMappers>(mappers)...) {
|
||||
}
|
||||
|
||||
template <typename ...Args>
|
||||
constexpr tuple_result<Args...> operator()(
|
||||
Args &&...args) const {
|
||||
constexpr auto kArity = sizeof...(Mappers);
|
||||
return call_helper(
|
||||
std::make_index_sequence<kArity>(),
|
||||
std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename ...Args, std::size_t ...I>
|
||||
inline tuple_result<Args...> call_helper(
|
||||
std::index_sequence<I...>,
|
||||
Args &&...args) const {
|
||||
return std::make_tuple(
|
||||
std::get<I>(_mappers)(std::forward<Args>(args)...)...);
|
||||
}
|
||||
|
||||
std::tuple<wrap_mapper_t<std::decay_t<Mappers>>...> _mappers;
|
||||
|
||||
};
|
||||
|
||||
template <typename ...Args>
|
||||
tuple_mapper<Args...> tuple(Args &&...args) {
|
||||
return tuple_mapper<Args...>(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
} // namespace details
|
||||
|
||||
namespace mappers {
|
||||
|
|
|
@ -71,12 +71,15 @@ void AbstractButton::mouseReleaseEvent(QMouseEvent *e) {
|
|||
onStateChanged(was, StateChangeSource::ByPress);
|
||||
if (was & StateFlag::Over) {
|
||||
_modifiers = e->modifiers();
|
||||
auto test = weak(this);
|
||||
if (_clickedCallback) {
|
||||
_clickedCallback();
|
||||
} else {
|
||||
emit clicked();
|
||||
}
|
||||
_clicks.fire({});
|
||||
if (test) {
|
||||
_clicks.fire({});
|
||||
}
|
||||
} else {
|
||||
setOver(false, StateChangeSource::ByHover);
|
||||
}
|
||||
|
|
55
Telegram/SourceFiles/ui/focus_persister.h
Normal file
55
Telegram/SourceFiles/ui/focus_persister.h
Normal file
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
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-2017 John Preston, https://desktop.telegram.org
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
namespace Ui {
|
||||
|
||||
class FocusPersister {
|
||||
public:
|
||||
FocusPersister(QWidget *parent, QWidget *steal = nullptr)
|
||||
: _weak(GrabFocused(parent)) {
|
||||
if (steal) {
|
||||
steal->setFocus();
|
||||
}
|
||||
}
|
||||
|
||||
~FocusPersister() {
|
||||
if (auto strong = _weak.data()) {
|
||||
if (auto window = strong->window()) {
|
||||
if (window->focusWidget() != strong) {
|
||||
strong->setFocus();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
static QWidget *GrabFocused(QWidget *parent) {
|
||||
if (auto window = parent ? parent->window() : nullptr) {
|
||||
return window->focusWidget();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
QPointer<QWidget> _weak;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Ui
|
|
@ -26,8 +26,16 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
|||
|
||||
namespace Ui {
|
||||
|
||||
template <typename Parent>
|
||||
class RpWidgetWrap : public Parent {
|
||||
template <typename Widget>
|
||||
using RpWidgetParent = std::conditional_t<
|
||||
std::is_same_v<Widget, QWidget>,
|
||||
TWidget,
|
||||
TWidgetHelper<Widget>>;
|
||||
|
||||
template <typename Widget>
|
||||
class RpWidgetWrap : public RpWidgetParent<Widget> {
|
||||
using Parent = RpWidgetParent<Widget>;
|
||||
|
||||
public:
|
||||
using Parent::Parent;
|
||||
|
||||
|
@ -77,6 +85,13 @@ public:
|
|||
return eventStreams().alive.events();
|
||||
}
|
||||
|
||||
void showOn(rpl::producer<bool> &&shown) {
|
||||
std::move(shown)
|
||||
| rpl::start([this](bool visible) {
|
||||
setVisible(visible);
|
||||
}, lifetime());
|
||||
}
|
||||
|
||||
rpl::lifetime &lifetime() {
|
||||
return _lifetime.data;
|
||||
}
|
||||
|
@ -110,7 +125,7 @@ protected:
|
|||
return eventHook(event);
|
||||
}
|
||||
virtual bool eventHook(QEvent *event) {
|
||||
return TWidget::event(event);
|
||||
return Parent::event(event);
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -139,9 +154,9 @@ private:
|
|||
|
||||
};
|
||||
|
||||
class RpWidget : public RpWidgetWrap<TWidget> {
|
||||
class RpWidget : public RpWidgetWrap<QWidget> {
|
||||
public:
|
||||
using RpWidgetWrap<TWidget>::RpWidgetWrap;
|
||||
using RpWidgetWrap<QWidget>::RpWidgetWrap;
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -319,7 +319,8 @@ void SplittedWidgetOther::paintEvent(QPaintEvent *e) {
|
|||
}
|
||||
}
|
||||
|
||||
ScrollArea::ScrollArea(QWidget *parent, const style::ScrollArea &st, bool handleTouch) : TWidgetHelper<QScrollArea>(parent)
|
||||
ScrollArea::ScrollArea(QWidget *parent, const style::ScrollArea &st, bool handleTouch)
|
||||
: RpWidgetWrap<QScrollArea>(parent)
|
||||
, _st(st)
|
||||
, _horizontalBar(this, false, &_st)
|
||||
, _verticalBar(this, true, &_st)
|
||||
|
|
|
@ -21,6 +21,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
|||
#pragma once
|
||||
|
||||
#include <rpl/event_stream.h>
|
||||
#include "ui/rp_widget.h"
|
||||
#include "styles/style_widgets.h"
|
||||
|
||||
namespace Ui {
|
||||
|
@ -171,7 +172,7 @@ private:
|
|||
};
|
||||
|
||||
class SplittedWidgetOther;
|
||||
class ScrollArea : public TWidgetHelper<QScrollArea> {
|
||||
class ScrollArea : public Ui::RpWidgetWrap<QScrollArea> {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
|
|
@ -1055,6 +1055,12 @@ MediaPlayerButton {
|
|||
ProfilePeerListItem {
|
||||
left: pixels;
|
||||
bottom: pixels;
|
||||
height: pixels;
|
||||
photoPosition: point;
|
||||
namePosition: point;
|
||||
statusPosition: point;
|
||||
photoSize: pixels;
|
||||
maximalWidth: pixels;
|
||||
|
||||
button: OutlineButton;
|
||||
statusFg: color;
|
||||
|
@ -1062,6 +1068,31 @@ ProfilePeerListItem {
|
|||
statusFgActive: color;
|
||||
}
|
||||
|
||||
defaultProfileMemberItem: ProfilePeerListItem {
|
||||
height: 58px;
|
||||
photoPosition: point(12px, 6px);
|
||||
namePosition: point(68px, 11px);
|
||||
statusPosition: point(68px, 31px);
|
||||
photoSize: 46px;
|
||||
button: OutlineButton {
|
||||
outlineWidth: 0px;
|
||||
|
||||
textBg: windowBg;
|
||||
textBgOver: windowBgOver;
|
||||
|
||||
textFg: windowSubTextFg;
|
||||
textFgOver: windowSubTextFgOver;
|
||||
|
||||
font: normalFont;
|
||||
padding: margins(11px, 5px, 11px, 5px);
|
||||
|
||||
ripple: defaultRippleAnimation;
|
||||
}
|
||||
statusFg: windowSubTextFg;
|
||||
statusFgOver: windowSubTextFgOver;
|
||||
statusFgActive: windowActiveTextFg;
|
||||
}
|
||||
|
||||
InfoTopBar {
|
||||
height: pixels;
|
||||
back: IconButton;
|
||||
|
|
|
@ -84,11 +84,10 @@ SlideWrap<RpWidget> *SlideWrap<RpWidget>::finishAnimations() {
|
|||
|
||||
SlideWrap<RpWidget> *SlideWrap<RpWidget>::toggleOn(
|
||||
rpl::producer<bool> &&shown) {
|
||||
_toggleOnLifetime.destroy();
|
||||
std::move(shown)
|
||||
| rpl::start([this](bool shown) {
|
||||
toggleAnimated(shown);
|
||||
}, _toggleOnLifetime);
|
||||
}, lifetime());
|
||||
finishAnimations();
|
||||
return this;
|
||||
}
|
||||
|
|
|
@ -77,7 +77,6 @@ private:
|
|||
|
||||
bool _shown = true;
|
||||
rpl::event_stream<bool> _shownUpdated;
|
||||
rpl::lifetime _toggleOnLifetime;
|
||||
Animation _slideAnimation;
|
||||
int _duration = 0;
|
||||
|
||||
|
|
|
@ -263,6 +263,9 @@ void TopBarWidget::paintEvent(QPaintEvent *e) {
|
|||
if (!_menuToggle->isHidden()) {
|
||||
decreaseWidth += _menuToggle->width();
|
||||
}
|
||||
if (!_infoToggle->isHidden()) {
|
||||
decreaseWidth += _infoToggle->width() + st::topBarSkip;
|
||||
}
|
||||
if (!_search->isHidden()) {
|
||||
decreaseWidth += _search->width();
|
||||
}
|
||||
|
|
|
@ -564,6 +564,7 @@
|
|||
<(src_loc)/ui/countryinput.h
|
||||
<(src_loc)/ui/emoji_config.cpp
|
||||
<(src_loc)/ui/emoji_config.h
|
||||
<(src_loc)/ui/focus_persister.h
|
||||
<(src_loc)/ui/images.cpp
|
||||
<(src_loc)/ui/images.h
|
||||
<(src_loc)/ui/rp_widget.h
|
||||
|
|
Loading…
Add table
Reference in a new issue