Support arrows in chat switch.

This commit is contained in:
John Preston
2025-08-26 14:19:36 +04:00
parent 6dcd52deb5
commit 00919f3232
4 changed files with 81 additions and 29 deletions

View File

@@ -648,6 +648,8 @@ bool Application::eventFilter(QObject *object, QEvent *e) {
if (base::Platform::GlobalShortcuts::IsToggleFullScreenKey(event)
&& toggleActiveWindowFullScreen()) {
return true;
} else if (Shortcuts::HandlePossibleChatSwitch(event)) {
return true;
}
} break;
case QEvent::MouseButtonPress:

View File

@@ -805,12 +805,22 @@ bool HandleEvent(
return Launch(Data.lookup(object));
}
void CancelChatSwitch(Qt::Key result) {
bool CancelChatSwitch(Qt::Key result) {
ChatSwitchModifier = Qt::Key();
if (ChatSwitchStarted) {
ChatSwitchStarted = false;
ChatSwitchStream.fire({ .action = result });
if (!ChatSwitchStarted) {
return false;
}
ChatSwitchStarted = false;
ChatSwitchStream.fire({ .action = result });
return true;
}
bool NavigateChatSwitch(Qt::Key result) {
if (!ChatSwitchStarted) {
return false;
}
ChatSwitchStream.fire({ .action = result });
return true;
}
rpl::producer<ChatSwitchRequest> ChatSwitchRequests() {
@@ -823,17 +833,9 @@ bool HandlePossibleChatSwitch(not_null<QKeyEvent*> event) {
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(QKeySequence(ctrl | Qt::Key_Tab))
&& (Data.handles(ctrl | Qt::ShiftModifier | Qt::Key_Backtab)
|| Data.handles(ctrl | Qt::ShiftModifier | Qt::Key_Tab)
@@ -872,6 +874,18 @@ bool HandlePossibleChatSwitch(not_null<QKeyEvent*> event) {
return true;
}
}
} else if (type == QEvent::KeyPress) {
const auto key = Qt::Key(event->key());
if (key == Qt::Key_Escape) {
return CancelChatSwitch(Qt::Key_Escape);
} else if (key == Qt::Key_Return || key == Qt::Key_Enter) {
return CancelChatSwitch(Qt::Key_Enter);
} else if (key == Qt::Key_Left
|| key == Qt::Key_Right
|| key == Qt::Key_Up
|| key == Qt::Key_Down) {
return NavigateChatSwitch(key);
}
} else if (type == QEvent::KeyRelease) {
const auto key = Qt::Key(event->key());
if (key == ChatSwitchModifier) {

View File

@@ -210,9 +210,8 @@ rpl::producer<> ChatSwitchProcess::closeRequests() const {
}
void ChatSwitchProcess::process(const Request &request) {
Expects(_selected < int(_list.size()));
Expects(_selected < _shownCount);
const auto count = int(_list.size());
if (request.action == Qt::Key_Escape) {
_closeRequests.fire({});
} else if (request.action == Qt::Key_Enter) {
@@ -221,18 +220,28 @@ void ChatSwitchProcess::process(const Request &request) {
} else {
_closeRequests.fire({});
}
} else if (request.action == Qt::Key_Tab) {
if (_selected < 0 || _selected + 1 >= count) {
} else if (request.action == Qt::Key_Tab
|| request.action == Qt::Key_Right) {
if (_selected < 0 || _selected + 1 >= _shownCount) {
setSelected(0);
} else {
setSelected(_selected + 1);
}
} else if (request.action == Qt::Key_Backtab) {
} else if (request.action == Qt::Key_Backtab
|| request.action == Qt::Key_Left) {
if (_selected <= 0) {
setSelected(count - 1);
setSelected(_shownCount - 1);
} else {
setSelected(_selected - 1);
}
} else if (request.action == Qt::Key_Up) {
const auto now = std::max(_selected, 0) - _shownPerRow;
const auto bound = (now < 0) ? (_shownCount + now) : now;
setSelected(bound);
} else if (request.action == Qt::Key_Down) {
const auto now = std::max(_selected, 0) + _shownPerRow;
const auto bound = (now >= _shownCount) ? (now - _shownCount) : now;
setSelected(bound);
}
}
@@ -357,17 +366,38 @@ void ChatSwitchProcess::layout(QSize size) {
auto inner = outer.marginsRemoved(st::chatSwitchPadding);
const auto available = inner.width();
const auto canPerRow = (available / st::chatSwitchSize.width());
const auto canRows = (canPerRow > 2 * 7)
? 1
: (canPerRow > 3 * 4)
? 2
: 3;
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();
_shownRows = std::min(canRows, (count + canPerRow - 1) / canPerRow);
_shownPerRow = std::min(count / _shownRows, canPerRow);
if (_shownRows > 2) {
if (_shownPerRow * 2 > _shownRows * 4) {
_shownRows = 2;
} else if (_shownPerRow > 4) {
_shownPerRow = 4;
}
}
if (_shownRows > 1) {
if (_shownPerRow > _shownRows * 7) {
_shownRows = 1;
} else if (_shownPerRow > 7) {
_shownPerRow = 7;
}
}
_shownCount = _shownPerRow * _shownRows;
if (_selected >= _shownCount) {
_selected = -1;
}
const auto width = _shownPerRow * st::chatSwitchSize.width();
const auto height = _shownRows * st::chatSwitchSize.height();
size = QSize(width, height);
_inner = QRect(
@@ -381,16 +411,19 @@ void ChatSwitchProcess::layout(QSize size) {
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) {
for (auto row = 0; row != _shownRows; ++row) {
auto left = padding.left();
for (auto column = 0; column != _shownPerRow; ++column) {
auto &entry = _entries[index++];
entry.button->moveToLeft(left, top, _inner.width());
entry.button->show();
left += st::chatSwitchSize.width();
}
top += st::chatSwitchSize.height();
}
for (auto i = _shownRows * _shownPerRow; i < count; ++i) {
_entries[i].button->hide();
}
_shadowed = _outer.marginsAdded(st::boxRoundShadow.extend);
_view->setGeometry(_shadowed);

View File

@@ -71,6 +71,9 @@ private:
std::vector<Entry> _entries;
int _selected = -1;
int _shownRows = 0;
int _shownCount = 0;
int _shownPerRow = 0;
rpl::event_stream<not_null<Data::Thread*>> _chosen;
rpl::event_stream<> _closeRequests;