mirror of
https://github.com/telegramdesktop/tdesktop
synced 2025-08-31 06:26:18 +00:00
First attempt of Ctrl+Tab/Ctrl+Shift+Tab UI.
This commit is contained in:
@@ -1663,6 +1663,8 @@ PRIVATE
|
|||||||
window/window_adaptive.h
|
window/window_adaptive.h
|
||||||
window/window_chat_preview.cpp
|
window/window_chat_preview.cpp
|
||||||
window/window_chat_preview.h
|
window/window_chat_preview.h
|
||||||
|
window/window_chat_switch_process.cpp
|
||||||
|
window/window_chat_switch_process.h
|
||||||
window/window_connecting_widget.cpp
|
window/window_connecting_widget.cpp
|
||||||
window/window_connecting_widget.h
|
window/window_connecting_widget.h
|
||||||
window/window_controller.cpp
|
window/window_controller.cpp
|
||||||
|
@@ -656,8 +656,20 @@ bool Application::eventFilter(QObject *object, QEvent *e) {
|
|||||||
updateNonIdle();
|
updateNonIdle();
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
|
case QEvent::KeyRelease: {
|
||||||
|
const auto event = static_cast<QKeyEvent*>(e);
|
||||||
|
if (Shortcuts::HandlePossibleChatSwitch(event)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
case QEvent::ShortcutOverride: {
|
case QEvent::ShortcutOverride: {
|
||||||
// handle shortcuts ourselves
|
// Ctrl+Tab/Ctrl+Shift+Tab chat switch is a special shortcut case,
|
||||||
|
// because it not only does an action on the shortcut activation,
|
||||||
|
// but also keeps the UI visible until you release the Ctrl key.
|
||||||
|
Shortcuts::HandlePossibleChatSwitch(static_cast<QKeyEvent*>(e));
|
||||||
|
|
||||||
|
// Handle all the shortcut management manually.
|
||||||
return true;
|
return true;
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
|
@@ -30,6 +30,10 @@ constexpr auto kCountLimit = 256; // How many shortcuts can be in json file.
|
|||||||
rpl::event_stream<not_null<Request*>> RequestsStream;
|
rpl::event_stream<not_null<Request*>> RequestsStream;
|
||||||
bool Paused/* = false*/;
|
bool Paused/* = false*/;
|
||||||
|
|
||||||
|
Qt::Key ChatSwitchModifier/* = Qt::Key()*/;
|
||||||
|
bool ChatSwitchStarted/* = false*/;
|
||||||
|
rpl::event_stream<ChatSwitchRequest> ChatSwitchStream;
|
||||||
|
|
||||||
const auto AutoRepeatCommands = base::flat_set<Command>{
|
const auto AutoRepeatCommands = base::flat_set<Command>{
|
||||||
Command::MediaPrevious,
|
Command::MediaPrevious,
|
||||||
Command::MediaNext,
|
Command::MediaNext,
|
||||||
@@ -156,6 +160,7 @@ public:
|
|||||||
void toggleMedia(bool toggled);
|
void toggleMedia(bool toggled);
|
||||||
void toggleSupport(bool toggled);
|
void toggleSupport(bool toggled);
|
||||||
void listen(not_null<QWidget*> widget);
|
void listen(not_null<QWidget*> widget);
|
||||||
|
[[nodiscard]] bool handles(const QKeySequence &sequence) const;
|
||||||
|
|
||||||
[[nodiscard]] const QStringList &errors() const;
|
[[nodiscard]] const QStringList &errors() const;
|
||||||
|
|
||||||
@@ -361,6 +366,10 @@ void Manager::listen(not_null<QWidget*> widget) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Manager::handles(const QKeySequence &sequence) const {
|
||||||
|
return _shortcuts.contains(sequence);
|
||||||
|
}
|
||||||
|
|
||||||
void Manager::pruneListened() {
|
void Manager::pruneListened() {
|
||||||
for (auto i = begin(_listened); i != end(_listened);) {
|
for (auto i = begin(_listened); i != end(_listened);) {
|
||||||
if (i->data()) {
|
if (i->data()) {
|
||||||
@@ -470,10 +479,6 @@ void Manager::fillDefaults() {
|
|||||||
set(u"ctrl+pgup"_q, Command::ChatPrevious);
|
set(u"ctrl+pgup"_q, Command::ChatPrevious);
|
||||||
set(u"alt+up"_q, Command::ChatPrevious);
|
set(u"alt+up"_q, Command::ChatPrevious);
|
||||||
|
|
||||||
set(u"%1+tab"_q.arg(ctrl), Command::ChatNext);
|
|
||||||
set(u"%1+shift+tab"_q.arg(ctrl), Command::ChatPrevious);
|
|
||||||
set(u"%1+backtab"_q.arg(ctrl), Command::ChatPrevious);
|
|
||||||
|
|
||||||
set(u"ctrl+alt+home"_q, Command::ChatFirst);
|
set(u"ctrl+alt+home"_q, Command::ChatFirst);
|
||||||
set(u"ctrl+alt+end"_q, Command::ChatLast);
|
set(u"ctrl+alt+end"_q, Command::ChatLast);
|
||||||
|
|
||||||
@@ -800,6 +805,72 @@ bool HandleEvent(
|
|||||||
return Launch(Data.lookup(object));
|
return Launch(Data.lookup(object));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CancelChatSwitch(Qt::Key result) {
|
||||||
|
ChatSwitchModifier = Qt::Key();
|
||||||
|
if (ChatSwitchStarted) {
|
||||||
|
ChatSwitchStarted = false;
|
||||||
|
ChatSwitchStream.fire({ .action = result });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rpl::producer<ChatSwitchRequest> ChatSwitchRequests() {
|
||||||
|
return ChatSwitchStream.events();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HandlePossibleChatSwitch(not_null<QKeyEvent*> event) {
|
||||||
|
const auto type = event->type();
|
||||||
|
if (Paused) {
|
||||||
|
return false;
|
||||||
|
} else if (type == QEvent::ShortcutOverride) {
|
||||||
|
const auto key = Qt::Key(event->key());
|
||||||
|
if (key == Qt::Key_Escape) {
|
||||||
|
CancelChatSwitch(Qt::Key_Escape);
|
||||||
|
return false;
|
||||||
|
} else if (key == Qt::Key_Return || key == Qt::Key_Enter) {
|
||||||
|
CancelChatSwitch(Qt::Key_Enter);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const auto ctrl = Platform::IsMac()
|
||||||
|
? Qt::MetaModifier
|
||||||
|
: Qt::ControlModifier;
|
||||||
|
|
||||||
|
if (Data.handles(ctrl | Qt::ShiftModifier | Qt::Key_Tab)
|
||||||
|
&& Data.handles(QKeySequence(ctrl | Qt::Key_Tab))
|
||||||
|
&& Data.handles(QKeySequence(ctrl | Qt::Key_Backtab))) {
|
||||||
|
return false;
|
||||||
|
} else if (key == Qt::Key_Control || key == Qt::Key_Meta) {
|
||||||
|
ChatSwitchModifier = key;
|
||||||
|
} else if (key == Qt::Key_Tab || key == Qt::Key_Backtab) {
|
||||||
|
const auto modifiers = event->modifiers();
|
||||||
|
if (modifiers & ctrl) {
|
||||||
|
if (Data.handles(modifiers | key)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (ChatSwitchModifier == Qt::Key()) {
|
||||||
|
ChatSwitchModifier = Platform::IsMac()
|
||||||
|
? Qt::Key_Meta
|
||||||
|
: Qt::Key_Control;
|
||||||
|
}
|
||||||
|
const auto action = (modifiers & Qt::ShiftModifier)
|
||||||
|
? Qt::Key_Backtab
|
||||||
|
: key;
|
||||||
|
const auto started = !std::exchange(ChatSwitchStarted, true);
|
||||||
|
ChatSwitchStream.fire({
|
||||||
|
.action = action,
|
||||||
|
.started = started,
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (type == QEvent::KeyRelease) {
|
||||||
|
const auto key = Qt::Key(event->key());
|
||||||
|
if (key == ChatSwitchModifier) {
|
||||||
|
CancelChatSwitch(Qt::Key_Enter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
void ToggleMediaShortcuts(bool toggled) {
|
void ToggleMediaShortcuts(bool toggled) {
|
||||||
Data.toggleMedia(toggled);
|
Data.toggleMedia(toggled);
|
||||||
}
|
}
|
||||||
|
@@ -7,6 +7,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
class QKeyEvent;
|
||||||
|
class QShortcutEvent;
|
||||||
|
|
||||||
namespace Shortcuts {
|
namespace Shortcuts {
|
||||||
|
|
||||||
enum class Command {
|
enum class Command {
|
||||||
@@ -122,7 +125,7 @@ private:
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
rpl::producer<not_null<Request*>> Requests();
|
[[nodiscard]] rpl::producer<not_null<Request*>> Requests();
|
||||||
|
|
||||||
void Start();
|
void Start();
|
||||||
void Finish();
|
void Finish();
|
||||||
@@ -132,7 +135,15 @@ void Listen(not_null<QWidget*> widget);
|
|||||||
bool Launch(Command command);
|
bool Launch(Command command);
|
||||||
bool HandleEvent(not_null<QObject*> object, not_null<QShortcutEvent*> event);
|
bool HandleEvent(not_null<QObject*> object, not_null<QShortcutEvent*> event);
|
||||||
|
|
||||||
const QStringList &Errors();
|
bool HandlePossibleChatSwitch(not_null<QKeyEvent*> event);
|
||||||
|
|
||||||
|
struct ChatSwitchRequest {
|
||||||
|
Qt::Key action = Qt::Key_Tab; // Key_Tab, Key_Backtab or Key_Escape.
|
||||||
|
bool started = false;
|
||||||
|
};
|
||||||
|
[[nodiscard]] rpl::producer<ChatSwitchRequest> ChatSwitchRequests();
|
||||||
|
|
||||||
|
[[nodiscard]] const QStringList &Errors();
|
||||||
|
|
||||||
// Media shortcuts are not enabled by default, because other
|
// Media shortcuts are not enabled by default, because other
|
||||||
// applications also use them. They are enabled only when
|
// applications also use them. They are enabled only when
|
||||||
|
@@ -7,6 +7,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||||||
*/
|
*/
|
||||||
#include "data/components/recent_peers.h"
|
#include "data/components/recent_peers.h"
|
||||||
|
|
||||||
|
#include "data/data_peer.h"
|
||||||
|
#include "data/data_session.h"
|
||||||
|
#include "history/history.h"
|
||||||
#include "main/main_session.h"
|
#include "main/main_session.h"
|
||||||
#include "storage/serialize_common.h"
|
#include "storage/serialize_common.h"
|
||||||
#include "storage/serialize_peer.h"
|
#include "storage/serialize_peer.h"
|
||||||
@@ -133,4 +136,28 @@ void RecentPeers::applyLocal(QByteArray serialized) {
|
|||||||
("Suggestions: RecentPeers read OK, count: %1").arg(_list.size()));
|
("Suggestions: RecentPeers read OK, count: %1").arg(_list.size()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<not_null<Thread*>> RecentPeers::collectChatOpenHistory() const {
|
||||||
|
_session->local().readSearchSuggestions();
|
||||||
|
|
||||||
|
auto result = _opens;
|
||||||
|
result.reserve(result.size() + _list.size());
|
||||||
|
for (const auto &peer : _list) {
|
||||||
|
const auto history = peer->owner().history(peer);
|
||||||
|
const auto thread = not_null<Data::Thread*>(history);
|
||||||
|
if (!ranges::contains(result, thread)) {
|
||||||
|
result.push_back(thread);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RecentPeers::chatOpenPush(not_null<Thread*> thread) {
|
||||||
|
const auto i = ranges::find(_opens, thread);
|
||||||
|
if (i == end(_opens)) {
|
||||||
|
_opens.insert(begin(_opens), thread);
|
||||||
|
} else if (i != begin(_opens)) {
|
||||||
|
ranges::rotate(begin(_opens), i, i + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Data
|
} // namespace Data
|
||||||
|
@@ -13,6 +13,8 @@ class Session;
|
|||||||
|
|
||||||
namespace Data {
|
namespace Data {
|
||||||
|
|
||||||
|
class Thread;
|
||||||
|
|
||||||
class RecentPeers final {
|
class RecentPeers final {
|
||||||
public:
|
public:
|
||||||
explicit RecentPeers(not_null<Main::Session*> session);
|
explicit RecentPeers(not_null<Main::Session*> session);
|
||||||
@@ -28,10 +30,16 @@ public:
|
|||||||
[[nodiscard]] QByteArray serialize() const;
|
[[nodiscard]] QByteArray serialize() const;
|
||||||
void applyLocal(QByteArray serialized);
|
void applyLocal(QByteArray serialized);
|
||||||
|
|
||||||
|
[[nodiscard]] auto collectChatOpenHistory() const
|
||||||
|
-> std::vector<not_null<Thread*>>;
|
||||||
|
void chatOpenPush(not_null<Thread*> thread);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const not_null<Main::Session*> _session;
|
const not_null<Main::Session*> _session;
|
||||||
|
|
||||||
std::vector<not_null<PeerData*>> _list;
|
std::vector<not_null<PeerData*>> _list;
|
||||||
|
std::vector<not_null<Thread*>> _opens;
|
||||||
|
|
||||||
rpl::event_stream<> _updates;
|
rpl::event_stream<> _updates;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@@ -331,6 +331,14 @@ ivHeightDefault: 800px;
|
|||||||
|
|
||||||
maxWidthSharedMediaWindow: 419px;
|
maxWidthSharedMediaWindow: 419px;
|
||||||
|
|
||||||
|
chatSwitchMargins: margins(16px, 16px, 16px, 16px);
|
||||||
|
chatSwitchPadding: margins(12px, 12px, 12px, 12px);
|
||||||
|
chatSwitchSize: size(72px, 72px);
|
||||||
|
chatSwitchUserpic: UserpicButton(defaultUserpicButton) {
|
||||||
|
size: size(56px, 56px);
|
||||||
|
photoSize: 56px;
|
||||||
|
}
|
||||||
|
|
||||||
// Windows specific
|
// Windows specific
|
||||||
|
|
||||||
winQuitIcon: icon {{ "win_quit", windowFg }};
|
winQuitIcon: icon {{ "win_quit", windowFg }};
|
||||||
|
225
Telegram/SourceFiles/window/window_chat_switch_process.cpp
Normal file
225
Telegram/SourceFiles/window/window_chat_switch_process.cpp
Normal file
@@ -0,0 +1,225 @@
|
|||||||
|
/*
|
||||||
|
This file is part of Telegram Desktop,
|
||||||
|
the official desktop application for the Telegram messaging service.
|
||||||
|
|
||||||
|
For license and copyright information please follow this link:
|
||||||
|
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
*/
|
||||||
|
#include "window/window_chat_switch_process.h"
|
||||||
|
|
||||||
|
#include "core/shortcuts.h"
|
||||||
|
#include "data/components/recent_peers.h"
|
||||||
|
#include "data/data_thread.h"
|
||||||
|
#include "main/main_session.h"
|
||||||
|
#include "ui/widgets/shadow.h"
|
||||||
|
#include "ui/controls/userpic_button.h"
|
||||||
|
#include "ui/rp_widget.h"
|
||||||
|
#include "styles/style_layers.h"
|
||||||
|
#include "styles/style_window.h"
|
||||||
|
|
||||||
|
namespace Window {
|
||||||
|
|
||||||
|
ChatSwitchProcess::ChatSwitchProcess(
|
||||||
|
not_null<Ui::RpWidget*> geometry,
|
||||||
|
not_null<Main::Session*> session,
|
||||||
|
Data::Thread *opened)
|
||||||
|
: _session(session)
|
||||||
|
, _widget(std::make_unique<Ui::RpWidget>(
|
||||||
|
geometry->parentWidget() ? geometry->parentWidget() : geometry))
|
||||||
|
, _view(Ui::CreateChild<Ui::RpWidget>(_widget.get()))\
|
||||||
|
, _bg(st::boxRadius, st::boxBg)
|
||||||
|
, _over(st::boxRadius, st::windowBgOver) {
|
||||||
|
setupWidget(geometry);
|
||||||
|
setupContent(opened);
|
||||||
|
setupView();
|
||||||
|
}
|
||||||
|
|
||||||
|
ChatSwitchProcess::~ChatSwitchProcess() = default;
|
||||||
|
|
||||||
|
rpl::producer<not_null<Data::Thread*>> ChatSwitchProcess::chosen() const {
|
||||||
|
return _chosen.events();
|
||||||
|
}
|
||||||
|
|
||||||
|
rpl::producer<> ChatSwitchProcess::closeRequests() const {
|
||||||
|
return _closeRequests.events();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatSwitchProcess::process(const Request &request) {
|
||||||
|
Expects(_selected < int(_list.size()));
|
||||||
|
|
||||||
|
const auto count = int(_list.size());
|
||||||
|
if (request.action == Qt::Key_Escape) {
|
||||||
|
_closeRequests.fire({});
|
||||||
|
} else if (request.action == Qt::Key_Enter) {
|
||||||
|
if (_selected >= 0) {
|
||||||
|
_chosen.fire_copy(_list[_selected]);
|
||||||
|
} else {
|
||||||
|
_closeRequests.fire({});
|
||||||
|
}
|
||||||
|
} else if (request.action == Qt::Key_Tab) {
|
||||||
|
if (_selected < 0 || _selected + 1 >= count) {
|
||||||
|
setSelected(0);
|
||||||
|
} else {
|
||||||
|
setSelected(_selected + 1);
|
||||||
|
}
|
||||||
|
} else if (request.action == Qt::Key_Backtab) {
|
||||||
|
if (_selected <= 0) {
|
||||||
|
setSelected(count - 1);
|
||||||
|
} else {
|
||||||
|
setSelected(_selected - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatSwitchProcess::setSelected(int index) {
|
||||||
|
if (_selected == index || _list.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (_selected >= 0) {
|
||||||
|
_entries[_selected].button->update();
|
||||||
|
}
|
||||||
|
_selected = index;
|
||||||
|
if (_selected >= 0) {
|
||||||
|
_entries[_selected].button->update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatSwitchProcess::setupWidget(not_null<Ui::RpWidget*> geometry) {
|
||||||
|
geometry->geometryValue(
|
||||||
|
) | rpl::start_with_next([=](QRect value) {
|
||||||
|
const auto parent = geometry->parentWidget();
|
||||||
|
_widget->setGeometry((parent == _widget->parentWidget())
|
||||||
|
? value
|
||||||
|
: QRect(QPoint(), value.size()));
|
||||||
|
}, _widget->lifetime());
|
||||||
|
|
||||||
|
_widget->events() | rpl::start_with_next([=](not_null<QEvent*> e) {
|
||||||
|
if (e->type() == QEvent::MouseButtonPress) {
|
||||||
|
_closeRequests.fire({});
|
||||||
|
}
|
||||||
|
}, _widget->lifetime());
|
||||||
|
|
||||||
|
_widget->show();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatSwitchProcess::setupContent(Data::Thread *opened) {
|
||||||
|
_list = _session->recentPeers().collectChatOpenHistory();
|
||||||
|
if (opened) {
|
||||||
|
const auto i = ranges::find(_list, not_null(opened));
|
||||||
|
if (i == end(_list)) {
|
||||||
|
_list.insert(begin(_list), opened);
|
||||||
|
} else if (i != begin(_list)) {
|
||||||
|
ranges::rotate(begin(_list), i, i + 1);
|
||||||
|
}
|
||||||
|
_selected = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto index = 0;
|
||||||
|
for (const auto &thread : _list) {
|
||||||
|
const auto button = Ui::CreateChild<Ui::AbstractButton>(_view.get());
|
||||||
|
button->resize(st::chatSwitchSize);
|
||||||
|
button->paintRequest() | rpl::start_with_next([=] {
|
||||||
|
if (index == _selected) {
|
||||||
|
auto p = QPainter(button);
|
||||||
|
_over.paint(p, button->rect());
|
||||||
|
}
|
||||||
|
}, button->lifetime());
|
||||||
|
button->setClickedCallback([=] {
|
||||||
|
_chosen.fire_copy(thread);
|
||||||
|
});
|
||||||
|
button->events() | rpl::start_with_next([=](not_null<QEvent*> e) {
|
||||||
|
if (e->type() == QEvent::MouseMove) {
|
||||||
|
setSelected(index);
|
||||||
|
}
|
||||||
|
}, button->lifetime());
|
||||||
|
button->show();
|
||||||
|
|
||||||
|
const auto userpic = Ui::CreateChild<Ui::UserpicButton>(
|
||||||
|
button,
|
||||||
|
thread->peer(),
|
||||||
|
st::chatSwitchUserpic);
|
||||||
|
userpic->show();
|
||||||
|
userpic->move(
|
||||||
|
((button->width() - userpic->width()) / 2),
|
||||||
|
((button->height() - userpic->height()) / 2));
|
||||||
|
userpic->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||||
|
|
||||||
|
_entries.push_back({ .button = button });
|
||||||
|
|
||||||
|
++index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatSwitchProcess::setupView() {
|
||||||
|
_widget->sizeValue() | rpl::start_with_next([=](QSize size) {
|
||||||
|
layout(size);
|
||||||
|
}, _view->lifetime());
|
||||||
|
_view->show();
|
||||||
|
|
||||||
|
_view->paintRequest() | rpl::start_with_next([=](QRect clip) {
|
||||||
|
if (_outer.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto p = QPainter(_view);
|
||||||
|
p.translate(-_shadowed.topLeft());
|
||||||
|
Ui::Shadow::paint(p, _outer, _view->width(), st::boxRoundShadow);
|
||||||
|
_bg.paint(p, _outer);
|
||||||
|
}, _view->lifetime());
|
||||||
|
|
||||||
|
_view->events() | rpl::start_with_next([=](not_null<QEvent*> e) {
|
||||||
|
if (e->type() == QEvent::MouseButtonPress) {
|
||||||
|
e->accept();
|
||||||
|
}
|
||||||
|
}, _view->lifetime());
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatSwitchProcess::layout(QSize size) {
|
||||||
|
const auto full = QRect(QPoint(), size);
|
||||||
|
const auto outer = full.marginsRemoved(st::chatSwitchMargins);
|
||||||
|
auto inner = outer.marginsRemoved(st::chatSwitchPadding);
|
||||||
|
const auto available = inner.width();
|
||||||
|
const auto canPerRow = (available / st::chatSwitchSize.width());
|
||||||
|
if (canPerRow < 1 || _list.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto count = int(_list.size());
|
||||||
|
const auto rows = (count + canPerRow - 1) / canPerRow;
|
||||||
|
const auto minPerRow = count / rows;
|
||||||
|
const auto wideRows = (count - (minPerRow * rows));
|
||||||
|
const auto maxPerRow = wideRows ? (minPerRow + 1) : minPerRow;
|
||||||
|
const auto narrowShift = wideRows ? (st::chatSwitchSize.width() / 2) : 0;
|
||||||
|
const auto width = maxPerRow * st::chatSwitchSize.width();
|
||||||
|
const auto height = rows * st::chatSwitchSize.height();
|
||||||
|
|
||||||
|
size = QSize(width, height);
|
||||||
|
_inner = QRect(
|
||||||
|
(full.width() - width) / 2,
|
||||||
|
(full.height() - height) / 2,
|
||||||
|
width,
|
||||||
|
height);
|
||||||
|
_outer = _inner.marginsAdded(st::chatSwitchPadding);
|
||||||
|
|
||||||
|
const auto padding = st::boxRoundShadow.extend + st::chatSwitchPadding;
|
||||||
|
|
||||||
|
auto index = 0;
|
||||||
|
auto top = padding.top();
|
||||||
|
for (auto row = 0; row != rows; ++row) {
|
||||||
|
const auto columns = (row < wideRows) ? maxPerRow : minPerRow;
|
||||||
|
auto left = padding.left() + ((row < wideRows) ? 0 : narrowShift);
|
||||||
|
for (auto column = 0; column != columns; ++column) {
|
||||||
|
auto &entry = _entries[index++];
|
||||||
|
entry.button->moveToLeft(left, top, _inner.width());
|
||||||
|
left += st::chatSwitchSize.width();
|
||||||
|
}
|
||||||
|
top += st::chatSwitchSize.height();
|
||||||
|
}
|
||||||
|
|
||||||
|
_shadowed = _outer.marginsAdded(st::boxRoundShadow.extend);
|
||||||
|
_view->setGeometry(_shadowed);
|
||||||
|
}
|
||||||
|
|
||||||
|
rpl::lifetime &ChatSwitchProcess::lifetime() {
|
||||||
|
return _lifetime;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Window
|
82
Telegram/SourceFiles/window/window_chat_switch_process.h
Normal file
82
Telegram/SourceFiles/window/window_chat_switch_process.h
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
/*
|
||||||
|
This file is part of Telegram Desktop,
|
||||||
|
the official desktop application for the Telegram messaging service.
|
||||||
|
|
||||||
|
For license and copyright information please follow this link:
|
||||||
|
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "ui/round_rect.h"
|
||||||
|
|
||||||
|
namespace Data {
|
||||||
|
class Thread;
|
||||||
|
} // namespace Data
|
||||||
|
|
||||||
|
namespace Main {
|
||||||
|
class Session;
|
||||||
|
} // namespace Main
|
||||||
|
|
||||||
|
namespace Shortcuts {
|
||||||
|
struct ChatSwitchRequest;
|
||||||
|
} // namespace Shortcuts
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
class AbstractButton;
|
||||||
|
class RpWidget;
|
||||||
|
} // namespace Ui
|
||||||
|
|
||||||
|
namespace Window {
|
||||||
|
|
||||||
|
class ChatSwitchProcess final {
|
||||||
|
public:
|
||||||
|
// Create widget in geometry->parentWidget() and geometry->geometry().
|
||||||
|
ChatSwitchProcess(
|
||||||
|
not_null<Ui::RpWidget*> geometry,
|
||||||
|
not_null<Main::Session*> session,
|
||||||
|
Data::Thread *opened);
|
||||||
|
~ChatSwitchProcess();
|
||||||
|
|
||||||
|
[[nodiscard]] rpl::producer<not_null<Data::Thread*>> chosen() const;
|
||||||
|
[[nodiscard]] rpl::producer<> closeRequests() const;
|
||||||
|
|
||||||
|
using Request = Shortcuts::ChatSwitchRequest;
|
||||||
|
void process(const Request &request);
|
||||||
|
|
||||||
|
[[nodiscard]] rpl::lifetime &lifetime();
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct Entry {
|
||||||
|
not_null<Ui::AbstractButton*> button;
|
||||||
|
};
|
||||||
|
void setupWidget(not_null<Ui::RpWidget*> geometry);
|
||||||
|
void setupContent(Data::Thread *opened);
|
||||||
|
void setupView();
|
||||||
|
|
||||||
|
void layout(QSize size);
|
||||||
|
|
||||||
|
void setSelected(int index);
|
||||||
|
|
||||||
|
const not_null<Main::Session*> _session;
|
||||||
|
const std::unique_ptr<Ui::RpWidget> _widget;
|
||||||
|
const not_null<Ui::RpWidget*> _view;
|
||||||
|
|
||||||
|
QRect _shadowed;
|
||||||
|
QRect _outer;
|
||||||
|
QRect _inner;
|
||||||
|
Ui::RoundRect _bg;
|
||||||
|
Ui::RoundRect _over;
|
||||||
|
|
||||||
|
std::vector<not_null<Data::Thread*>> _list;
|
||||||
|
std::vector<Entry> _entries;
|
||||||
|
|
||||||
|
int _selected = -1;
|
||||||
|
|
||||||
|
rpl::event_stream<not_null<Data::Thread*>> _chosen;
|
||||||
|
rpl::event_stream<> _closeRequests;
|
||||||
|
|
||||||
|
rpl::lifetime _lifetime;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Window
|
@@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||||||
#include "boxes/peers/replace_boost_box.h"
|
#include "boxes/peers/replace_boost_box.h"
|
||||||
#include "boxes/delete_messages_box.h"
|
#include "boxes/delete_messages_box.h"
|
||||||
#include "window/window_chat_preview.h"
|
#include "window/window_chat_preview.h"
|
||||||
|
#include "window/window_chat_switch_process.h"
|
||||||
#include "window/window_controller.h"
|
#include "window/window_controller.h"
|
||||||
#include "window/window_filters_menu.h"
|
#include "window/window_filters_menu.h"
|
||||||
#include "window/window_separate_id.h"
|
#include "window/window_separate_id.h"
|
||||||
@@ -32,6 +33,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||||||
#include "history/view/history_view_subsection_tabs.h"
|
#include "history/view/history_view_subsection_tabs.h"
|
||||||
#include "media/player/media_player_instance.h"
|
#include "media/player/media_player_instance.h"
|
||||||
#include "media/view/media_view_open_common.h"
|
#include "media/view/media_view_open_common.h"
|
||||||
|
#include "data/components/recent_peers.h"
|
||||||
#include "data/stickers/data_custom_emoji.h"
|
#include "data/stickers/data_custom_emoji.h"
|
||||||
#include "data/data_document_resolver.h"
|
#include "data/data_document_resolver.h"
|
||||||
#include "data/data_download_manager.h"
|
#include "data/data_download_manager.h"
|
||||||
@@ -1735,18 +1737,50 @@ void SessionController::init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void SessionController::setupShortcuts() {
|
void SessionController::setupShortcuts() {
|
||||||
Shortcuts::Requests(
|
using namespace Shortcuts;
|
||||||
|
|
||||||
|
ChatSwitchRequests(
|
||||||
|
) | rpl::filter([=](const ChatSwitchRequest &request) {
|
||||||
|
return !window().locked()
|
||||||
|
&& (_chatSwitchProcess
|
||||||
|
|| (request.started
|
||||||
|
&& (Core::App().activeWindow() == &window())));
|
||||||
|
}) | rpl::start_with_next([=](const ChatSwitchRequest &request) {
|
||||||
|
if (!_chatSwitchProcess) {
|
||||||
|
_chatSwitchProcess = std::make_unique<ChatSwitchProcess>(
|
||||||
|
widget()->bodyWidget(),
|
||||||
|
&session(),
|
||||||
|
activeChatCurrent().thread());
|
||||||
|
const auto close = [this, raw = _chatSwitchProcess.get()] {
|
||||||
|
if (_chatSwitchProcess.get() == raw) {
|
||||||
|
base::take(_chatSwitchProcess);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
_chatSwitchProcess->chosen(
|
||||||
|
) | rpl::start_with_next([=](not_null<Data::Thread*> thread) {
|
||||||
|
close();
|
||||||
|
jumpToChatListEntry({ Dialogs::Key(thread), FullMsgId() });
|
||||||
|
}, _chatSwitchProcess->lifetime());
|
||||||
|
|
||||||
|
_chatSwitchProcess->closeRequests(
|
||||||
|
) | rpl::start_with_next(close, _chatSwitchProcess->lifetime());
|
||||||
|
}
|
||||||
|
_chatSwitchProcess->process(request);
|
||||||
|
}, _lifetime);
|
||||||
|
|
||||||
|
Requests(
|
||||||
) | rpl::filter([=] {
|
) | rpl::filter([=] {
|
||||||
return (Core::App().activeWindow() == &window())
|
return (Core::App().activeWindow() == &window())
|
||||||
&& !isLayerShown()
|
&& !isLayerShown()
|
||||||
&& !window().locked();
|
&& !window().locked();
|
||||||
}) | rpl::start_with_next([=](not_null<Shortcuts::Request*> request) {
|
}) | rpl::start_with_next([=](not_null<Request*> request) {
|
||||||
using C = Shortcuts::Command;
|
using C = Command;
|
||||||
|
|
||||||
const auto app = &Core::App();
|
const auto app = &Core::App();
|
||||||
const auto accountsCount = int(app->domain().accounts().size());
|
const auto accountsCount = int(app->domain().accounts().size());
|
||||||
auto &&accounts = ranges::views::zip(
|
auto &&accounts = ranges::views::zip(
|
||||||
Shortcuts::kShowAccount,
|
kShowAccount,
|
||||||
ranges::views::ints(0, accountsCount));
|
ranges::views::ints(0, accountsCount));
|
||||||
for (const auto &[command, index] : accounts) {
|
for (const auto &[command, index] : accounts) {
|
||||||
request->check(command) && request->handle([=] {
|
request->check(command) && request->handle([=] {
|
||||||
@@ -2033,6 +2067,10 @@ void SessionController::setActiveChatEntry(Dialogs::RowDescriptor row) {
|
|||||||
{ anim::type::normal, anim::activation::background });
|
{ anim::type::normal, anim::activation::background });
|
||||||
}, _activeHistoryLifetime);
|
}, _activeHistoryLifetime);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (const auto thread = row.key.thread()) {
|
||||||
|
session().recentPeers().chatOpenPush(thread);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (session().supportMode()) {
|
if (session().supportMode()) {
|
||||||
pushToChatEntryHistory(row);
|
pushToChatEntryHistory(row);
|
||||||
|
@@ -99,6 +99,7 @@ class SectionMemento;
|
|||||||
class Controller;
|
class Controller;
|
||||||
class FiltersMenu;
|
class FiltersMenu;
|
||||||
class ChatPreviewManager;
|
class ChatPreviewManager;
|
||||||
|
class ChatSwitchProcess;
|
||||||
|
|
||||||
struct PeerByLinkInfo;
|
struct PeerByLinkInfo;
|
||||||
struct SeparateId;
|
struct SeparateId;
|
||||||
@@ -784,6 +785,8 @@ private:
|
|||||||
std::deque<std::shared_ptr<Ui::ChatTheme>> _lastUsedCustomChatThemes;
|
std::deque<std::shared_ptr<Ui::ChatTheme>> _lastUsedCustomChatThemes;
|
||||||
rpl::variable<PeerThemeOverride> _peerThemeOverride;
|
rpl::variable<PeerThemeOverride> _peerThemeOverride;
|
||||||
|
|
||||||
|
std::unique_ptr<ChatSwitchProcess> _chatSwitchProcess;
|
||||||
|
|
||||||
base::has_weak_ptr _storyOpenGuard;
|
base::has_weak_ptr _storyOpenGuard;
|
||||||
|
|
||||||
QString _premiumRef;
|
QString _premiumRef;
|
||||||
|
Reference in New Issue
Block a user