mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 06:33:57 +02:00
Use PeerList for sessions list (wip).
This commit is contained in:
parent
7bf557971e
commit
168711b352
2 changed files with 316 additions and 230 deletions
|
@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "base/algorithm.h"
|
#include "base/algorithm.h"
|
||||||
#include "base/platform/base_platform_info.h"
|
#include "base/platform/base_platform_info.h"
|
||||||
#include "boxes/self_destruction_box.h"
|
#include "boxes/self_destruction_box.h"
|
||||||
|
#include "boxes/peer_lists_box.h"
|
||||||
#include "ui/boxes/confirm_box.h"
|
#include "ui/boxes/confirm_box.h"
|
||||||
#include "lang/lang_keys.h"
|
#include "lang/lang_keys.h"
|
||||||
#include "main/main_session.h"
|
#include "main/main_session.h"
|
||||||
|
@ -57,6 +58,50 @@ enum class Type {
|
||||||
Other,
|
Other,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class Row;
|
||||||
|
|
||||||
|
class RowDelegate {
|
||||||
|
public:
|
||||||
|
virtual void rowUpdateRow(not_null<Row*> row) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Row final : public PeerListRow {
|
||||||
|
public:
|
||||||
|
Row(not_null<RowDelegate*> delegate, const EntryData &data);
|
||||||
|
|
||||||
|
void update(const EntryData &data);
|
||||||
|
void updateName(const QString &name);
|
||||||
|
|
||||||
|
[[nodiscard]] EntryData data() const;
|
||||||
|
|
||||||
|
QString generateName() override;
|
||||||
|
QString generateShortName() override;
|
||||||
|
PaintRoundImageCallback generatePaintUserpicCallback() override;
|
||||||
|
|
||||||
|
int elementsCount() const override;
|
||||||
|
QRect elementGeometry(int element, int outerWidth) const override;
|
||||||
|
bool elementDisabled(int element) const override;
|
||||||
|
bool elementOnlySelect(int element) const override;
|
||||||
|
void elementAddRipple(
|
||||||
|
int element,
|
||||||
|
QPoint point,
|
||||||
|
Fn<void()> updateCallback) override;
|
||||||
|
void elementsStopLastRipple() override;
|
||||||
|
void elementsPaint(
|
||||||
|
Painter &p,
|
||||||
|
int outerWidth,
|
||||||
|
bool selected,
|
||||||
|
int selectedElement) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
const not_null<RowDelegate*> _delegate;
|
||||||
|
Ui::Text::String _location;
|
||||||
|
Type _type = Type::Other;
|
||||||
|
EntryData _data;
|
||||||
|
QImage _userpic;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
void RenameBox(not_null<Ui::GenericBox*> box) {
|
void RenameBox(not_null<Ui::GenericBox*> box) {
|
||||||
box->setTitle(tr::lng_settings_rename_device_title());
|
box->setTitle(tr::lng_settings_rename_device_title());
|
||||||
|
|
||||||
|
@ -255,7 +300,7 @@ void RenameBox(not_null<Ui::GenericBox*> box) {
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] QImage GenerateUserpic(Type type) {
|
[[nodiscard]] QImage GenerateUserpic(Type type) {
|
||||||
const auto size = st::sessionUserpicSize;
|
const auto size = st::sessionListItem.photoSize;
|
||||||
const auto full = size * style::DevicePixelRatio();
|
const auto full = size * style::DevicePixelRatio();
|
||||||
const auto rect = QRect(0, 0, size, size);
|
const auto rect = QRect(0, 0, size, size);
|
||||||
|
|
||||||
|
@ -440,6 +485,125 @@ void SessionInfoBox(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Row::Row(not_null<RowDelegate*> delegate, const EntryData &data)
|
||||||
|
: PeerListRow(data.hash)
|
||||||
|
, _delegate(delegate)
|
||||||
|
, _location(st::defaultTextStyle, LocationAndDate(data))
|
||||||
|
, _type(TypeFromEntry(data))
|
||||||
|
, _data(data)
|
||||||
|
, _userpic(GenerateUserpic(_type)) {
|
||||||
|
setCustomStatus(_data.info);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Row::update(const EntryData &data) {
|
||||||
|
_data = data;
|
||||||
|
setCustomStatus(_data.info);
|
||||||
|
refreshName(st::sessionListItem);
|
||||||
|
_location.setText(st::defaultTextStyle, LocationAndDate(_data));
|
||||||
|
_delegate->rowUpdateRow(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Row::updateName(const QString &name) {
|
||||||
|
_data.name = name;
|
||||||
|
refreshName(st::sessionListItem);
|
||||||
|
_delegate->rowUpdateRow(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
EntryData Row::data() const {
|
||||||
|
return _data;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Row::generateName() {
|
||||||
|
return _data.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Row::generateShortName() {
|
||||||
|
return generateName();
|
||||||
|
}
|
||||||
|
|
||||||
|
PaintRoundImageCallback Row::generatePaintUserpicCallback() {
|
||||||
|
return [=](
|
||||||
|
Painter &p,
|
||||||
|
int x,
|
||||||
|
int y,
|
||||||
|
int outerWidth,
|
||||||
|
int size) {
|
||||||
|
p.drawImage(x, y, _userpic);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
int Row::elementsCount() const {
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
QRect Row::elementGeometry(int element, int outerWidth) const {
|
||||||
|
switch (element) {
|
||||||
|
case 1: {
|
||||||
|
return QRect(
|
||||||
|
st::sessionListItem.namePosition.x(),
|
||||||
|
st::sessionLocationTop,
|
||||||
|
outerWidth,
|
||||||
|
st::normalFont->height);
|
||||||
|
} break;
|
||||||
|
case 2: {
|
||||||
|
const auto size = QSize(
|
||||||
|
st::sessionListThreeDotsIcon.width(),
|
||||||
|
st::sessionListThreeDotsIcon.height());
|
||||||
|
const auto margins = QMargins(
|
||||||
|
0,
|
||||||
|
(st::sessionListItem.height - size.height()) / 2,
|
||||||
|
st::sessionListThreeDotsSkip,
|
||||||
|
0);
|
||||||
|
const auto right = margins.right();
|
||||||
|
const auto top = margins.top();
|
||||||
|
const auto left = outerWidth - right - size.width();
|
||||||
|
return QRect(QPoint(left, top), size);
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
return QRect();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Row::elementDisabled(int element) const {
|
||||||
|
return !id() || (element == 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Row::elementOnlySelect(int element) const {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Row::elementAddRipple(
|
||||||
|
int element,
|
||||||
|
QPoint point,
|
||||||
|
Fn<void()> updateCallback) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void Row::elementsStopLastRipple() {
|
||||||
|
}
|
||||||
|
|
||||||
|
void Row::elementsPaint(
|
||||||
|
Painter &p,
|
||||||
|
int outerWidth,
|
||||||
|
bool selected,
|
||||||
|
int selectedElement) {
|
||||||
|
if (id()) {
|
||||||
|
const auto geometry = elementGeometry(2, outerWidth);
|
||||||
|
const auto &icon = (selectedElement == 2)
|
||||||
|
? st::sessionListThreeDotsIconOver
|
||||||
|
: st::sessionListThreeDotsIcon;
|
||||||
|
icon.paint(p, geometry.x(), geometry.y(), outerWidth);
|
||||||
|
}
|
||||||
|
p.setFont(st::msgFont);
|
||||||
|
p.setPen(st::sessionInfoFg);
|
||||||
|
const auto locationLeft = st::sessionListItem.namePosition.x();
|
||||||
|
const auto available = outerWidth - locationLeft;
|
||||||
|
_location.drawLeftElided(
|
||||||
|
p,
|
||||||
|
locationLeft,
|
||||||
|
st::sessionLocationTop,
|
||||||
|
available,
|
||||||
|
outerWidth);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
class SessionsContent : public Ui::RpWidget {
|
class SessionsContent : public Ui::RpWidget {
|
||||||
|
@ -455,25 +619,13 @@ protected:
|
||||||
void paintEvent(QPaintEvent *e) override;
|
void paintEvent(QPaintEvent *e) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct Entry {
|
|
||||||
Entry() = default;
|
|
||||||
explicit Entry(const EntryData &entry);
|
|
||||||
|
|
||||||
EntryData data;
|
|
||||||
|
|
||||||
bool incomplete = false;
|
|
||||||
Type type = Type::Other;
|
|
||||||
TimeId activeTime = 0;
|
|
||||||
Ui::Text::String name, info, location;
|
|
||||||
QImage userpic;
|
|
||||||
};
|
|
||||||
struct Full {
|
struct Full {
|
||||||
Entry current;
|
EntryData current;
|
||||||
std::vector<Entry> incomplete;
|
std::vector<EntryData> incomplete;
|
||||||
std::vector<Entry> list;
|
std::vector<EntryData> list;
|
||||||
};
|
};
|
||||||
class Inner;
|
class Inner;
|
||||||
class List;
|
class ListController;
|
||||||
|
|
||||||
void shortPollSessions();
|
void shortPollSessions();
|
||||||
void parse(const Api::Authorizations::List &list);
|
void parse(const Api::Authorizations::List &list);
|
||||||
|
@ -495,44 +647,39 @@ private:
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class SessionsContent::List : public Ui::RpWidget {
|
class SessionsContent::ListController final
|
||||||
|
: public PeerListController
|
||||||
|
, public RowDelegate
|
||||||
|
, public base::has_weak_ptr {
|
||||||
public:
|
public:
|
||||||
List(QWidget *parent);
|
explicit ListController(not_null<Main::Session*> session);
|
||||||
|
|
||||||
void showData(gsl::span<const Entry> items);
|
Main::Session &session() const override;
|
||||||
|
void prepare() override;
|
||||||
|
void rowClicked(not_null<PeerListRow*> row) override;
|
||||||
|
void rowElementClicked(not_null<PeerListRow*> row, int element) override;
|
||||||
|
|
||||||
|
void rowUpdateRow(not_null<Row*> row) override;
|
||||||
|
|
||||||
|
void showData(gsl::span<const EntryData> items);
|
||||||
rpl::producer<int> itemsCount() const;
|
rpl::producer<int> itemsCount() const;
|
||||||
rpl::producer<uint64> terminateRequests() const;
|
rpl::producer<uint64> terminateRequests() const;
|
||||||
[[nodiscard]] rpl::producer<EntryData> showRequests() const;
|
[[nodiscard]] rpl::producer<EntryData> showRequests() const;
|
||||||
|
|
||||||
void terminating(uint64 hash, bool terminating);
|
[[nodiscard]] static std::unique_ptr<ListController> Add(
|
||||||
|
not_null<Ui::VerticalLayout*> container,
|
||||||
protected:
|
not_null<Main::Session*> session,
|
||||||
void resizeEvent(QResizeEvent *e) override;
|
style::margins margins = {});
|
||||||
void paintEvent(QPaintEvent *e) override;
|
|
||||||
|
|
||||||
void mousePressEvent(QMouseEvent *e) override;
|
|
||||||
void mouseReleaseEvent(QMouseEvent *e) override;
|
|
||||||
|
|
||||||
int resizeGetHeight(int newWidth) override;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct RowWidth {
|
|
||||||
int available = 0;
|
|
||||||
int info = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
void subscribeToCustomDeviceModel();
|
void subscribeToCustomDeviceModel();
|
||||||
void computeRowWidth();
|
|
||||||
|
|
||||||
RowWidth _rowWidth;
|
const not_null<Main::Session*> _session;
|
||||||
std::vector<Entry> _items;
|
|
||||||
std::map<uint64, std::unique_ptr<Ui::IconButton>> _terminateButtons;
|
|
||||||
rpl::event_stream<uint64> _terminateRequests;
|
rpl::event_stream<uint64> _terminateRequests;
|
||||||
rpl::event_stream<int> _itemsCount;
|
rpl::event_stream<int> _itemsCount;
|
||||||
rpl::event_stream<EntryData> _showRequests;
|
rpl::event_stream<EntryData> _showRequests;
|
||||||
|
|
||||||
int _pressed = -1;
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class SessionsContent::Inner : public Ui::RpWidget {
|
class SessionsContent::Inner : public Ui::RpWidget {
|
||||||
|
@ -546,32 +693,20 @@ public:
|
||||||
[[nodiscard]] rpl::producer<EntryData> showRequests() const;
|
[[nodiscard]] rpl::producer<EntryData> showRequests() const;
|
||||||
[[nodiscard]] rpl::producer<uint64> terminateOne() const;
|
[[nodiscard]] rpl::producer<uint64> terminateOne() const;
|
||||||
[[nodiscard]] rpl::producer<> terminateAll() const;
|
[[nodiscard]] rpl::producer<> terminateAll() const;
|
||||||
[[nodiscard]] rpl::producer<> renameCurrentRequests() const;
|
|
||||||
|
|
||||||
void terminatingOne(uint64 hash, bool terminating);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void setupContent();
|
void setupContent();
|
||||||
|
|
||||||
const not_null<Window::SessionController*> _controller;
|
const not_null<Window::SessionController*> _controller;
|
||||||
QPointer<List> _current;
|
std::unique_ptr<ListController> _current;
|
||||||
QPointer<Ui::SettingsButton> _terminateAll;
|
QPointer<Ui::SettingsButton> _terminateAll;
|
||||||
QPointer<List> _incomplete;
|
std::unique_ptr<ListController> _incomplete;
|
||||||
QPointer<List> _list;
|
std::unique_ptr<ListController> _list;
|
||||||
rpl::variable<int> _ttlDays;
|
rpl::variable<int> _ttlDays;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
SessionsContent::Entry::Entry(const EntryData &entry)
|
//, location(st::sessionInfoStyle, LocationAndDate(entry))
|
||||||
: data(entry)
|
|
||||||
, incomplete(entry.incomplete)
|
|
||||||
, type(TypeFromEntry(entry))
|
|
||||||
, activeTime(entry.activeTime)
|
|
||||||
, name(st::sessionNameStyle, entry.name)
|
|
||||||
, info(st::sessionInfoStyle, entry.info)
|
|
||||||
, location(st::sessionInfoStyle, LocationAndDate(entry))
|
|
||||||
, userpic(GenerateUserpic(type)) {
|
|
||||||
};
|
|
||||||
|
|
||||||
SessionsContent::SessionsContent(
|
SessionsContent::SessionsContent(
|
||||||
QWidget*,
|
QWidget*,
|
||||||
|
@ -629,20 +764,19 @@ void SessionsContent::parse(const Api::Authorizations::List &list) {
|
||||||
}
|
}
|
||||||
_data = Full();
|
_data = Full();
|
||||||
for (const auto &auth : list) {
|
for (const auto &auth : list) {
|
||||||
auto entry = Entry(auth);
|
if (!auth.hash) {
|
||||||
if (!entry.data.hash) {
|
_data.current = auth;
|
||||||
_data.current = std::move(entry);
|
} else if (auth.incomplete) {
|
||||||
} else if (entry.incomplete) {
|
_data.incomplete.push_back(auth);
|
||||||
_data.incomplete.push_back(std::move(entry));
|
|
||||||
} else {
|
} else {
|
||||||
_data.list.push_back(std::move(entry));
|
_data.list.push_back(auth);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_loading = false;
|
_loading = false;
|
||||||
|
|
||||||
ranges::sort(_data.list, std::greater<>(), &Entry::activeTime);
|
ranges::sort(_data.list, std::greater<>(), &EntryData::activeTime);
|
||||||
ranges::sort(_data.incomplete, std::greater<>(), &Entry::activeTime);
|
ranges::sort(_data.incomplete, std::greater<>(), &EntryData::activeTime);
|
||||||
|
|
||||||
_inner->showData(_data);
|
_inner->showData(_data);
|
||||||
|
|
||||||
|
@ -710,13 +844,12 @@ void SessionsContent::terminateOne(uint64 hash) {
|
||||||
if (mtpIsFalse(result)) {
|
if (mtpIsFalse(result)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_inner->terminatingOne(hash, false);
|
const auto removeByHash = [&](std::vector<EntryData> &list) {
|
||||||
const auto removeByHash = [&](std::vector<Entry> &list) {
|
|
||||||
list.erase(
|
list.erase(
|
||||||
ranges::remove(
|
ranges::remove(
|
||||||
list,
|
list,
|
||||||
hash,
|
hash,
|
||||||
[](const Entry &entry) { return entry.data.hash; }),
|
[](const EntryData &entry) { return entry.hash; }),
|
||||||
end(list));
|
end(list));
|
||||||
};
|
};
|
||||||
removeByHash(_data.incomplete);
|
removeByHash(_data.incomplete);
|
||||||
|
@ -724,13 +857,11 @@ void SessionsContent::terminateOne(uint64 hash) {
|
||||||
_inner->showData(_data);
|
_inner->showData(_data);
|
||||||
});
|
});
|
||||||
auto fail = crl::guard(weak, [=](const MTP::Error &error) {
|
auto fail = crl::guard(weak, [=](const MTP::Error &error) {
|
||||||
_inner->terminatingOne(hash, false);
|
|
||||||
});
|
});
|
||||||
_authorizations->requestTerminate(
|
_authorizations->requestTerminate(
|
||||||
std::move(done),
|
std::move(done),
|
||||||
std::move(fail),
|
std::move(fail),
|
||||||
hash);
|
hash);
|
||||||
_inner->terminatingOne(hash, true);
|
|
||||||
};
|
};
|
||||||
terminate(std::move(callback), tr::lng_settings_reset_one_sure(tr::now));
|
terminate(std::move(callback), tr::lng_settings_reset_one_sure(tr::now));
|
||||||
}
|
}
|
||||||
|
@ -788,8 +919,10 @@ void SessionsContent::Inner::setupContent() {
|
||||||
Ui::show(Box(RenameBox), Ui::LayerOption::KeepOther);
|
Ui::show(Box(RenameBox), Ui::LayerOption::KeepOther);
|
||||||
});
|
});
|
||||||
|
|
||||||
_current = content->add(
|
const auto session = &_controller->session();
|
||||||
object_ptr<List>(content),
|
_current = ListController::Add(
|
||||||
|
content,
|
||||||
|
session,
|
||||||
style::margins{ 0, 0, 0, st::sessionCurrentSkip });
|
style::margins{ 0, 0, 0, st::sessionCurrentSkip });
|
||||||
const auto terminateWrap = content->add(
|
const auto terminateWrap = content->add(
|
||||||
object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
|
object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
|
||||||
|
@ -814,7 +947,7 @@ void SessionsContent::Inner::setupContent() {
|
||||||
const auto incompleteInner = incompleteWrap->entity();
|
const auto incompleteInner = incompleteWrap->entity();
|
||||||
AddSkip(incompleteInner, st::sessionSubtitleSkip);
|
AddSkip(incompleteInner, st::sessionSubtitleSkip);
|
||||||
AddSubsectionTitle(incompleteInner, tr::lng_sessions_incomplete());
|
AddSubsectionTitle(incompleteInner, tr::lng_sessions_incomplete());
|
||||||
_incomplete = incompleteInner->add(object_ptr<List>(incompleteInner));
|
_incomplete = ListController::Add(incompleteInner, session);
|
||||||
AddSkip(incompleteInner);
|
AddSkip(incompleteInner);
|
||||||
AddDividerText(incompleteInner, tr::lng_sessions_incomplete_about());
|
AddDividerText(incompleteInner, tr::lng_sessions_incomplete_about());
|
||||||
|
|
||||||
|
@ -825,7 +958,7 @@ void SessionsContent::Inner::setupContent() {
|
||||||
const auto listInner = listWrap->entity();
|
const auto listInner = listWrap->entity();
|
||||||
AddSkip(listInner, st::sessionSubtitleSkip);
|
AddSkip(listInner, st::sessionSubtitleSkip);
|
||||||
AddSubsectionTitle(listInner, tr::lng_sessions_other_header());
|
AddSubsectionTitle(listInner, tr::lng_sessions_other_header());
|
||||||
_list = listInner->add(object_ptr<List>(listInner));
|
_list = ListController::Add(listInner, session);
|
||||||
AddSkip(listInner);
|
AddSkip(listInner);
|
||||||
AddDividerText(listInner, tr::lng_sessions_about_apps());
|
AddDividerText(listInner, tr::lng_sessions_about_apps());
|
||||||
|
|
||||||
|
@ -897,165 +1030,113 @@ rpl::producer<EntryData> SessionsContent::Inner::showRequests() const {
|
||||||
_list->showRequests());
|
_list->showRequests());
|
||||||
}
|
}
|
||||||
|
|
||||||
void SessionsContent::Inner::terminatingOne(uint64 hash, bool terminating) {
|
SessionsContent::ListController::ListController(
|
||||||
_incomplete->terminating(hash, terminating);
|
not_null<Main::Session*> session)
|
||||||
_list->terminating(hash, terminating);
|
: _session(session) {
|
||||||
}
|
}
|
||||||
|
|
||||||
SessionsContent::List::List(QWidget *parent) : RpWidget(parent) {
|
Main::Session &SessionsContent::ListController::session() const {
|
||||||
setAttribute(Qt::WA_OpaquePaintEvent);
|
return *_session;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SessionsContent::List::resizeEvent(QResizeEvent *e) {
|
void SessionsContent::ListController::subscribeToCustomDeviceModel() {
|
||||||
RpWidget::resizeEvent(e);
|
|
||||||
|
|
||||||
computeRowWidth();
|
|
||||||
}
|
|
||||||
|
|
||||||
void SessionsContent::List::subscribeToCustomDeviceModel() {
|
|
||||||
Core::App().settings().deviceModelChanges(
|
Core::App().settings().deviceModelChanges(
|
||||||
) | rpl::start_with_next([=](const QString &model) {
|
) | rpl::start_with_next([=](const QString &model) {
|
||||||
for (auto &entry : _items) {
|
for (auto i = 0; i != delegate()->peerListFullRowsCount(); ++i) {
|
||||||
if (!entry.data.hash) {
|
const auto row = delegate()->peerListRowAt(i);
|
||||||
entry.name.setText(st::sessionNameStyle, model);
|
if (!row->id()) {
|
||||||
|
static_cast<Row*>(row.get())->updateName(model);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
update();
|
|
||||||
}, lifetime());
|
}, lifetime());
|
||||||
}
|
}
|
||||||
|
|
||||||
void SessionsContent::List::showData(gsl::span<const Entry> items) {
|
void SessionsContent::ListController::prepare() {
|
||||||
computeRowWidth();
|
}
|
||||||
|
|
||||||
auto buttons = base::take(_terminateButtons);
|
void SessionsContent::ListController::rowClicked(
|
||||||
_items.clear();
|
not_null<PeerListRow*> row) {
|
||||||
_items.insert(begin(_items), items.begin(), items.end());
|
_showRequests.fire_copy(static_cast<Row*>(row.get())->data());
|
||||||
for (const auto &entry : _items) {
|
}
|
||||||
const auto hash = entry.data.hash;
|
|
||||||
if (!hash) {
|
void SessionsContent::ListController::rowElementClicked(
|
||||||
|
not_null<PeerListRow*> row,
|
||||||
|
int element) {
|
||||||
|
if (element == 1) {
|
||||||
|
if (const auto hash = static_cast<Row*>(row.get())->data().hash) {
|
||||||
|
_terminateRequests.fire_copy(hash);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SessionsContent::ListController::rowUpdateRow(not_null<Row*> row) {
|
||||||
|
delegate()->peerListUpdateRow(row);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SessionsContent::ListController::showData(
|
||||||
|
gsl::span<const EntryData> items) {
|
||||||
|
auto index = 0;
|
||||||
|
auto positions = base::flat_map<uint64, int>();
|
||||||
|
positions.reserve(items.size());
|
||||||
|
for (const auto &entry : items) {
|
||||||
|
const auto id = entry.hash;
|
||||||
|
positions.emplace(id, index++);
|
||||||
|
if (const auto row = delegate()->peerListFindRow(id)) {
|
||||||
|
static_cast<Row*>(row)->update(entry);
|
||||||
|
} else {
|
||||||
|
delegate()->peerListAppendRow(
|
||||||
|
std::make_unique<Row>(this, entry));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (auto i = 0; i != delegate()->peerListFullRowsCount();) {
|
||||||
|
const auto row = delegate()->peerListRowAt(i);
|
||||||
|
if (positions.contains(row->id())) {
|
||||||
|
++i;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const auto button = [&] {
|
delegate()->peerListRemoveRow(row);
|
||||||
const auto i = buttons.find(hash);
|
|
||||||
return _terminateButtons.emplace(
|
|
||||||
hash,
|
|
||||||
(i != end(buttons)
|
|
||||||
? std::move(i->second)
|
|
||||||
: std::make_unique<Ui::IconButton>(
|
|
||||||
this,
|
|
||||||
st::sessionTerminate))).first->second.get();
|
|
||||||
}();
|
|
||||||
button->setClickedCallback([=] {
|
|
||||||
_terminateRequests.fire_copy(hash);
|
|
||||||
});
|
|
||||||
button->show();
|
|
||||||
const auto number = _terminateButtons.size() - 1;
|
|
||||||
widthValue(
|
|
||||||
) | rpl::start_with_next([=] {
|
|
||||||
button->moveToRight(
|
|
||||||
st::sessionTerminateSkip,
|
|
||||||
(number * st::sessionHeight + st::sessionTerminateTop));
|
|
||||||
}, lifetime());
|
|
||||||
}
|
}
|
||||||
resizeToWidth(width());
|
delegate()->peerListSortRows([&](
|
||||||
_itemsCount.fire(_items.size());
|
const PeerListRow &a,
|
||||||
|
const PeerListRow &b) {
|
||||||
|
return positions[a.id()] < positions[b.id()];
|
||||||
|
});
|
||||||
|
delegate()->peerListRefreshRows();
|
||||||
|
_itemsCount.fire(delegate()->peerListFullRowsCount());
|
||||||
}
|
}
|
||||||
|
|
||||||
rpl::producer<EntryData> SessionsContent::List::showRequests() const {
|
rpl::producer<int> SessionsContent::ListController::itemsCount() const {
|
||||||
return _showRequests.events();
|
return _itemsCount.events_starting_with(
|
||||||
|
delegate()->peerListFullRowsCount());
|
||||||
}
|
}
|
||||||
|
|
||||||
rpl::producer<int> SessionsContent::List::itemsCount() const {
|
rpl::producer<uint64> SessionsContent::ListController::terminateRequests() const {
|
||||||
return _itemsCount.events_starting_with(_items.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
rpl::producer<uint64> SessionsContent::List::terminateRequests() const {
|
|
||||||
return _terminateRequests.events();
|
return _terminateRequests.events();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SessionsContent::List::terminating(uint64 hash, bool terminating) {
|
rpl::producer<EntryData> SessionsContent::ListController::showRequests() const {
|
||||||
const auto i = _terminateButtons.find(hash);
|
return _showRequests.events();
|
||||||
if (i != _terminateButtons.cend()) {
|
|
||||||
if (terminating) {
|
|
||||||
i->second->clearState();
|
|
||||||
i->second->hide();
|
|
||||||
} else {
|
|
||||||
i->second->show();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int SessionsContent::List::resizeGetHeight(int newWidth) {
|
auto SessionsContent::ListController::Add(
|
||||||
return _items.size() * st::sessionHeight;
|
not_null<Ui::VerticalLayout*> container,
|
||||||
}
|
not_null<Main::Session*> session,
|
||||||
|
style::margins margins)
|
||||||
void SessionsContent::List::computeRowWidth() {
|
-> std::unique_ptr<ListController> {
|
||||||
const auto available = width()
|
auto &lifetime = container->lifetime();
|
||||||
- st::sessionPadding.left()
|
const auto delegate = lifetime.make_state<
|
||||||
- st::sessionTerminateSkip;
|
PeerListContentDelegateSimple
|
||||||
_rowWidth = {
|
>();
|
||||||
.available = available,
|
auto controller = std::make_unique<ListController>(session);
|
||||||
.info = available - st::sessionTerminate.width,
|
controller->setStyleOverrides(&st::sessionList);
|
||||||
};
|
const auto content = container->add(
|
||||||
}
|
object_ptr<PeerListContent>(
|
||||||
|
container,
|
||||||
void SessionsContent::List::paintEvent(QPaintEvent *e) {
|
controller.get()),
|
||||||
QRect r(e->rect());
|
margins);
|
||||||
Painter p(this);
|
delegate->setContent(content);
|
||||||
|
controller->setDelegate(delegate);
|
||||||
p.fillRect(r, st::boxBg);
|
return controller;
|
||||||
p.setFont(st::linkFont);
|
|
||||||
const auto count = int(_items.size());
|
|
||||||
const auto from = floorclamp(r.y(), st::sessionHeight, 0, count);
|
|
||||||
const auto till = ceilclamp(
|
|
||||||
r.y() + r.height(),
|
|
||||||
st::sessionHeight,
|
|
||||||
0,
|
|
||||||
count);
|
|
||||||
|
|
||||||
const auto available = _rowWidth.available;
|
|
||||||
const auto x = st::sessionPadding.left();
|
|
||||||
const auto y = st::sessionPadding.top();
|
|
||||||
const auto w = width();
|
|
||||||
p.translate(0, from * st::sessionHeight);
|
|
||||||
for (auto i = from; i != till; ++i) {
|
|
||||||
const auto &entry = _items[i];
|
|
||||||
|
|
||||||
p.drawImage(st::sessionUserpicPosition, entry.userpic);
|
|
||||||
|
|
||||||
const auto nameW = _rowWidth.info;
|
|
||||||
const auto infoW = entry.data.hash ? _rowWidth.info : available;
|
|
||||||
|
|
||||||
p.setPen(st::sessionNameFg);
|
|
||||||
entry.name.drawLeftElided(p, x, y, nameW, w);
|
|
||||||
|
|
||||||
p.setPen(st::boxTextFg);
|
|
||||||
entry.info.drawLeftElided(p, x, y + st::sessionInfoTop, infoW, w);
|
|
||||||
|
|
||||||
p.setPen(st::sessionInfoFg);
|
|
||||||
entry.location.drawLeftElided(
|
|
||||||
p,
|
|
||||||
x,
|
|
||||||
y + st::sessionLocationTop,
|
|
||||||
available,
|
|
||||||
w);
|
|
||||||
|
|
||||||
p.translate(0, st::sessionHeight);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SessionsContent::List::mousePressEvent(QMouseEvent *e) {
|
|
||||||
const auto index = e->pos().y() / st::sessionHeight;
|
|
||||||
_pressed = (index >= 0 && index < _items.size()) ? index : -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SessionsContent::List::mouseReleaseEvent(QMouseEvent *e) {
|
|
||||||
const auto index = e->pos().y() / st::sessionHeight;
|
|
||||||
const auto released = (index >= 0 && index < _items.size()) ? index : -1;
|
|
||||||
if (released == _pressed && released >= 0) {
|
|
||||||
_showRequests.fire_copy(_items[released].data);
|
|
||||||
}
|
|
||||||
_pressed = -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SessionsBox::SessionsBox(
|
SessionsBox::SessionsBox(
|
||||||
|
|
|
@ -249,23 +249,13 @@ sessionsTerminateAll: SettingsButton(defaultSettingsButton) {
|
||||||
}
|
}
|
||||||
sessionsTerminateAllIcon: icon {{ "settings/devices/terminate_all", attentionButtonFg }};
|
sessionsTerminateAllIcon: icon {{ "settings/devices/terminate_all", attentionButtonFg }};
|
||||||
sessionsTerminateAllIconLeft: 30px;
|
sessionsTerminateAllIconLeft: 30px;
|
||||||
sessionHeight: 84px;
|
sessionLocationTop: 54px;
|
||||||
sessionInfoTop: 21px;
|
|
||||||
sessionLocationTop: 43px;
|
|
||||||
sessionCurrentSkip: 8px;
|
sessionCurrentSkip: 8px;
|
||||||
sessionSubtitleSkip: 14px;
|
sessionSubtitleSkip: 14px;
|
||||||
sessionPadding: margins(77px, 11px, 22px, 0px);
|
sessionLocationFg: windowSubTextFg;
|
||||||
sessionNameFont: msgNameFont;
|
|
||||||
sessionNameFg: boxTextFg;
|
|
||||||
sessionWhenFont: msgDateFont;
|
|
||||||
sessionWhenFg: windowSubTextFg;
|
|
||||||
sessionInfoFont: msgFont;
|
|
||||||
sessionInfoFg: windowSubTextFg;
|
sessionInfoFg: windowSubTextFg;
|
||||||
sessionTerminateTop: 9px;
|
sessionTerminateTop: 9px;
|
||||||
sessionTerminateSkip: 22px;
|
sessionTerminateSkip: 22px;
|
||||||
sessionUserpicSize: 42px;
|
|
||||||
sessionUserpicPosition: point(21px, 10px);
|
|
||||||
sessionNamePadding: margins(0px, 0px, 5px, 0px);
|
|
||||||
sessionTerminate: IconButton {
|
sessionTerminate: IconButton {
|
||||||
width: 20px;
|
width: 20px;
|
||||||
height: 20px;
|
height: 20px;
|
||||||
|
@ -280,15 +270,6 @@ sessionTerminate: IconButton {
|
||||||
color: windowBgOver;
|
color: windowBgOver;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sessionNameStyle: TextStyle(defaultTextStyle) {
|
|
||||||
font: sessionNameFont;
|
|
||||||
}
|
|
||||||
sessionWhenStyle: TextStyle(defaultTextStyle) {
|
|
||||||
font: sessionWhenFont;
|
|
||||||
}
|
|
||||||
sessionInfoStyle: TextStyle(defaultTextStyle) {
|
|
||||||
font: sessionInfoFont;
|
|
||||||
}
|
|
||||||
sessionIconWindows: icon{{ "settings/devices/device_desktop_win", historyPeerUserpicFg }};
|
sessionIconWindows: icon{{ "settings/devices/device_desktop_win", historyPeerUserpicFg }};
|
||||||
sessionIconMac: icon{{ "settings/devices/device_desktop_mac", historyPeerUserpicFg }};
|
sessionIconMac: icon{{ "settings/devices/device_desktop_mac", historyPeerUserpicFg }};
|
||||||
sessionIconUbuntu: icon{{ "settings/devices/device_linux_ubuntu", historyPeerUserpicFg }};
|
sessionIconUbuntu: icon{{ "settings/devices/device_linux_ubuntu", historyPeerUserpicFg }};
|
||||||
|
@ -327,3 +308,27 @@ sessionValueLabel: FlatLabel(defaultFlatLabel) {
|
||||||
textFg: windowSubTextFg;
|
textFg: windowSubTextFg;
|
||||||
}
|
}
|
||||||
sessionValueSkip: 8px;
|
sessionValueSkip: 8px;
|
||||||
|
|
||||||
|
sessionListItem: PeerListItem(defaultPeerListItem) {
|
||||||
|
button: OutlineButton(defaultPeerListButton) {
|
||||||
|
font: normalFont;
|
||||||
|
padding: margins(11px, 5px, 11px, 5px);
|
||||||
|
}
|
||||||
|
height: 84px;
|
||||||
|
photoPosition: point(21px, 10px);
|
||||||
|
nameStyle: TextStyle(defaultTextStyle) {
|
||||||
|
font: msgNameFont;
|
||||||
|
}
|
||||||
|
namePosition: point(77px, 11px);
|
||||||
|
statusPosition: point(77px, 32px);
|
||||||
|
photoSize: 42px;
|
||||||
|
statusFg: boxTextFg;
|
||||||
|
statusFgOver: boxTextFg;
|
||||||
|
}
|
||||||
|
sessionList: PeerList(defaultPeerList) {
|
||||||
|
item: sessionListItem;
|
||||||
|
padding: margins(0px, 4px, 0px, 0px);
|
||||||
|
}
|
||||||
|
sessionListThreeDotsIcon: icon {{ "info/edit/dotsmini", dialogsMenuIconFg }};
|
||||||
|
sessionListThreeDotsIconOver: icon {{ "info/edit/dotsmini", dialogsMenuIconFgOver }};
|
||||||
|
sessionListThreeDotsSkip: 12px;
|
||||||
|
|
Loading…
Add table
Reference in a new issue