Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Telegram/Resources/langs/lang.strings
Original file line number Diff line number Diff line change
Expand Up @@ -7919,5 +7919,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_mac_hold_to_quit" = "Hold {text} to Quit";

"lng_sr_country_column_name" = "Country name";
"lng_sr_languages_column_native" = "Native name";
"lng_sr_languages_column_name" = "Language name";

// Keys finished
189 changes: 189 additions & 0 deletions Telegram/SourceFiles/boxes/language_box.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ For license and copyright information please follow this link:

#include "data/data_peer_values.h"
#include "lang/lang_keys.h"
#include "base/screen_reader_state.h"
#include "ui/accessible/ui_accessible_item.h"
#include "ui/boxes/choose_language_box.h"
#include "ui/widgets/checkbox.h"
#include "ui/widgets/buttons.h"
Expand Down Expand Up @@ -51,6 +53,8 @@ For license and copyright information please follow this link:
#include "styles/style_menu_icons.h"
#include "styles/style_settings.h"

#include <unordered_map>

#include <QtGui/QGuiApplication>
#include <QtGui/QClipboard>

Expand All @@ -71,22 +75,101 @@ class Rows : public Ui::RpWidget {

int count() const;
int selected() const;
int chosenIndex() const;
void setSelected(int selected);
rpl::producer<bool> hasSelection() const;
rpl::producer<bool> isEmpty() const;

void activateSelected();
void selectSkip(int dir);
rpl::producer<Language> activations() const;
void changeChosen(const QString &chosen);

Ui::ScrollToRequest rowScrollRequest(int index) const;

static int DefaultRowHeight();

QAccessible::Role accessibilityRole() override {
return QAccessible::List;
}

QAccessible::Role accessibilityChildRole() const override {
return QAccessible::RadioButton;
}

QAccessible::State accessibilityChildState(int index) const override {
QAccessible::State state;
if (base::ScreenReaderState::Instance()->active()) {
state.focusable = true;
}
state.checkable = true;
state.checked = (index == chosenIndex());
if (index == selected()) {
state.active = true;
if (hasFocus()) {
state.focused = true;
}
}
return state;
}

int accessibilityChildCount() const override {
return count();
}

QString accessibilityChildName(int index) const override {
if (index < 0 || index >= count()) {
return {};
}
const auto &row = rowByIndex(index);
// Announce native name followed by English name.
return row.data.nativeName + u", "_q + row.data.name;
}

QRect accessibilityChildRect(int index) const override {
if (index < 0 || index >= count()) {
return QRect();
}
const auto &row = rowByIndex(index);
return QRect(0, row.top, width(), row.height);
}

int accessibilityChildColumnCount(int row) const override {
return 2;
}

QAccessible::Role accessibilityChildSubItemRole() const override {
return QAccessible::Cell;
}

QString accessibilityChildSubItemName(int row, int column) const override {
if (column == 0) {
return tr::lng_sr_languages_column_native(tr::now);
} else if (column == 1) {
return tr::lng_sr_languages_column_name(tr::now);
}
return {};
}

QString accessibilityChildSubItemValue(int row, int column) const override {
if (row < 0 || row >= count()) {
return {};
}
const auto &data = rowByIndex(row).data;
if (column == 0) {
return data.nativeName;
} else if (column == 1) {
return data.name;
}
return {};
}

protected:
int resizeGetHeight(int newWidth) override;

void focusInEvent(QFocusEvent *e) override;
void paintEvent(QPaintEvent *e) override;
void keyPressEvent(QKeyEvent *e) override;
void mouseMoveEvent(QMouseEvent *e) override;
void mousePressEvent(QMouseEvent *e) override;
void mouseReleaseEvent(QMouseEvent *e) override;
Expand Down Expand Up @@ -288,6 +371,63 @@ Rows::Rows(
resizeToWidth(width());
setAttribute(Qt::WA_MouseTracking);
update();

setAccessibleName(tr::lng_languages(tr::now));

base::ScreenReaderState::Instance()->activeValue(
) | rpl::on_next([=](bool active) {
setFocusPolicy(active ? Qt::TabFocus : Qt::NoFocus);
}, lifetime());
}

void Rows::focusInEvent(QFocusEvent *e) {
// Select first item or chosen item when focus enters.
if (selected() < 0 && count() > 0) {
const auto chosen = chosenIndex();
setSelected(chosen >= 0 ? chosen : 0);
}

RpWidget::focusInEvent(e);

if (base::ScreenReaderState::Instance()->active()) {
const auto index = selected();
if (index >= 0) {
InvokeQueued(this, [=] {
if (selected() != index || !hasFocus()) {
return;
}
accessibilityChildFocused(index);
});
}
}
}

void Rows::keyPressEvent(QKeyEvent *e) {
const auto key = e->key();
if (key == Qt::Key_Down) {
selectSkip(1);
} else if (key == Qt::Key_Up) {
selectSkip(-1);
} else if (key == Qt::Key_PageDown || key == Qt::Key_PageUp) {
const auto visibleHeight = visibleRegion().boundingRect().height();
const auto rowsPerPage = std::max(visibleHeight / DefaultRowHeight(), 1);
selectSkip(key == Qt::Key_PageDown ? rowsPerPage : -rowsPerPage);
} else if (key == Qt::Key_Home) {
if (count() > 0) {
setSelected(0);
}
} else if (key == Qt::Key_End) {
if (count() > 0) {
setSelected(count() - 1);
}
} else if (!e->isAutoRepeat()
&& (key == Qt::Key_Space
|| key == Qt::Key_Return
|| key == Qt::Key_Enter)) {
activateSelected();
} else {
RpWidget::keyPressEvent(e);
}
}

void Rows::mouseMoveEvent(QMouseEvent *e) {
Expand Down Expand Up @@ -555,7 +695,12 @@ void Rows::setForceRippled(not_null<Row*> row, bool rippled) {
}

void Rows::activateByIndex(int index) {
_chosen = rowByIndex(index).data.id;
_activations.fire_copy(rowByIndex(index).data);
if (base::ScreenReaderState::Instance()->active()) {
accessibilityChildStateChanged(index, { .checked = true });
accessibilityChildNameChanged(index);
}
}

void Rows::leaveEventHook(QEvent *e) {
Expand Down Expand Up @@ -631,21 +776,60 @@ int Rows::selected() const {
return indexFromSelection(_selected);
}

int Rows::chosenIndex() const {
for (auto i = 0, n = count(); i < n; ++i) {
if (rowByIndex(i).data.id == _chosen) {
return i;
}
}
return -1;
}

void Rows::activateSelected() {
const auto index = selected();
if (index >= 0) {
activateByIndex(index);
}
}

void Rows::selectSkip(int dir) {
const auto limit = count();
auto now = selected();
// If no keyboard selection, start from the checked item.
if (now < 0) {
now = chosenIndex();
}
if (now >= 0) {
const auto changed = now + dir;
if (changed < 0) {
setSelected(0);
} else if (changed >= limit) {
setSelected(limit - 1);
} else {
setSelected(changed);
}
} else if (dir > 0) {
setSelected(0);
}
}

rpl::producer<Language> Rows::activations() const {
return _activations.events();
}

void Rows::changeChosen(const QString &chosen) {
const auto oldIndex = chosenIndex();
_chosen = chosen;
for (const auto &row : _rows) {
row.check->setChecked(row.data.id == chosen, anim::type::normal);
}
if (base::ScreenReaderState::Instance()->active()) {
const auto newIndex = chosenIndex();
if (newIndex != oldIndex && newIndex >= 0) {
accessibilityChildStateChanged(newIndex, { .checked = true });
accessibilityChildNameChanged(newIndex);
}
}
}

void Rows::setSelected(int selected) {
Expand All @@ -656,6 +840,11 @@ void Rows::setSelected(int selected) {
} else {
updateSelected({});
}
if (selected >= 0 && selected < limit
&& base::ScreenReaderState::Instance()->active()) {
accessibilityChildNameChanged(selected);
accessibilityChildFocused(selected);
}
}

rpl::producer<bool> Rows::hasSelection() const {
Expand Down