diff --git a/Telegram/SourceFiles/boxes/language_box.cpp b/Telegram/SourceFiles/boxes/language_box.cpp index fb6ff36cca..d7844fb5bc 100644 --- a/Telegram/SourceFiles/boxes/language_box.cpp +++ b/Telegram/SourceFiles/boxes/language_box.cpp @@ -7,56 +7,55 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "boxes/language_box.h" -#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" -#include "ui/widgets/labels.h" -#include "ui/widgets/multi_select.h" -#include "ui/widgets/scroll_area.h" -#include "ui/widgets/dropdown_menu.h" -#include "ui/widgets/box_content_divider.h" -#include "ui/text/text_entity.h" -#include "ui/wrap/vertical_layout.h" -#include "ui/wrap/slide_wrap.h" -#include "ui/effects/ripple_animation.h" -#include "ui/toast/toast.h" -#include "ui/text/text_options.h" -#include "ui/painter.h" -#include "ui/vertical_list.h" -#include "ui/ui_utility.h" -#include "storage/localstorage.h" +#include "base/platform/base_platform_info.h" #include "boxes/abstract_box.h" #include "boxes/premium_preview_box.h" #include "boxes/translate_box.h" -#include "ui/boxes/confirm_box.h" -#include "main/main_session.h" -#include "mainwidget.h" -#include "mainwindow.h" #include "core/application.h" -#include "base/platform/base_platform_info.h" -#include "lang/lang_instance.h" +#include "data/data_peer_values.h" #include "lang/lang_cloud_manager.h" +#include "lang/lang_instance.h" +#include "lang/lang_keys.h" +#include "main/main_session.h" #include "platform/platform_translate_provider.h" #include "settings/settings_common.h" #include "spellcheck/spellcheck_types.h" +#include "storage/localstorage.h" +#include "ui/accessible/ui_accessible_item.h" +#include "ui/boxes/choose_language_box.h" +#include "ui/boxes/confirm_box.h" +#include "ui/effects/ripple_animation.h" +#include "ui/text/text_entity.h" +#include "ui/text/text_options.h" +#include "ui/toast/toast.h" +#include "ui/widgets/box_content_divider.h" +#include "ui/widgets/buttons.h" +#include "ui/widgets/checkbox.h" +#include "ui/widgets/dropdown_menu.h" +#include "ui/widgets/labels.h" +#include "ui/widgets/multi_select.h" +#include "ui/widgets/scroll_area.h" +#include "ui/wrap/slide_wrap.h" +#include "ui/wrap/vertical_layout.h" +#include "ui/painter.h" +#include "ui/screen_reader_mode.h" +#include "ui/ui_utility.h" +#include "ui/vertical_list.h" #include "window/window_controller.h" #include "window/window_session_controller.h" -#include "styles/style_layers.h" +#include "mainwidget.h" +#include "mainwindow.h" + #include "styles/style_boxes.h" -#include "styles/style_info.h" -#include "styles/style_passport.h" #include "styles/style_chat_helpers.h" +#include "styles/style_info.h" +#include "styles/style_layers.h" #include "styles/style_menu_icons.h" +#include "styles/style_passport.h" #include "styles/style_settings.h" -#include - -#include #include +#include namespace { @@ -89,80 +88,17 @@ public: 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 {}; - } + QAccessible::Role accessibilityRole() override; + Qt::FocusPolicy accessibilityFocusPolicy() override; + QAccessible::Role accessibilityChildRole() const override; + QAccessible::State accessibilityChildState(int index) const override; + int accessibilityChildCount() const override; + QString accessibilityChildName(int index) const override; + QRect accessibilityChildRect(int index) const override; + int accessibilityChildColumnCount(int row) const override; + QAccessible::Role accessibilityChildSubItemRole() const override; + QString accessibilityChildSubItemName(int row, int column) const override; + QString accessibilityChildSubItemValue(int row, int column) const override; protected: int resizeGetHeight(int newWidth) override; @@ -237,6 +173,13 @@ private: void repaintChecked(not_null row); void activateByIndex(int index); + enum class Announce { + No, + OnChange, + Always, + }; + void setSelected(int index, Announce announce); + void showMenu(int index); void setForceRippled(not_null row, bool rippled); bool canShare(not_null row) const; @@ -266,6 +209,26 @@ private: }; +[[nodiscard]] bool ForwardListNavigation( + not_null e, + not_null rows, + int pageHeight) { + const auto key = e->key(); + if (key == Qt::Key_Down) { + rows->selectSkip(1); + } else if (key == Qt::Key_Up) { + rows->selectSkip(-1); + } else if (key == Qt::Key_PageDown || key == Qt::Key_PageUp) { + const auto perPage = std::max( + pageHeight / Rows::DefaultRowHeight(), + 1); + rows->selectSkip((key == Qt::Key_PageDown) ? perPage : -perPage); + } else { + return false; + } + return true; +} + class Content : public Ui::RpWidget { public: Content( @@ -373,53 +336,34 @@ Rows::Rows( 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); + setSelected(chosen >= 0 ? chosen : 0, Announce::No); } - RpWidget::focusInEvent(e); - - if (base::ScreenReaderState::Instance()->active()) { - const auto index = selected(); - if (index >= 0) { - InvokeQueued(this, [=] { - if (selected() != index || !hasFocus()) { - return; - } + const auto index = selected(); + if (index >= 0) { + InvokeQueued(this, [=] { + if (selected() == index && hasFocus()) { accessibilityChildFocused(index); - }); - } + } + }); } } void Rows::keyPressEvent(QKeyEvent *e) { + const auto pageHeight = window() ? window()->height() : height(); + if (ForwardListNavigation(e, this, pageHeight)) { + return; + } 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); - } + if (key == Qt::Key_Home && count() > 0) { + setSelected(0, Announce::Always); + } else if (key == Qt::Key_End && count() > 0) { + setSelected(count() - 1, Announce::Always); } else if (!e->isAutoRepeat() && (key == Qt::Key_Space || key == Qt::Key_Return @@ -697,10 +641,8 @@ void Rows::setForceRippled(not_null 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); - } + accessibilityChildStateChanged(index, { .checked = true }); + accessibilityChildNameChanged(index); } void Rows::leaveEventHook(QEvent *e) { @@ -795,21 +737,20 @@ void Rows::activateSelected() { 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); + setSelected(0, Announce::Always); } else if (changed >= limit) { - setSelected(limit - 1); + setSelected(limit - 1, Announce::Always); } else { - setSelected(changed); + setSelected(changed, Announce::Always); } } else if (dir > 0) { - setSelected(0); + setSelected(0, Announce::Always); } } @@ -823,27 +764,35 @@ void Rows::changeChosen(const QString &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); - } + const auto newIndex = chosenIndex(); + if (newIndex != oldIndex && newIndex >= 0) { + accessibilityChildStateChanged(newIndex, { .checked = true }); + accessibilityChildNameChanged(newIndex); } } void Rows::setSelected(int selected) { + setSelected(selected, Announce::OnChange); +} + +void Rows::setSelected(int selected, Announce announce) { _mouseSelection = false; const auto limit = count(); - if (selected >= 0 && selected < limit) { - updateSelected(RowSelection{ selected }); + const auto clamped = (selected >= 0 && selected < limit) + ? selected + : -1; + const auto changed = (indexFromSelection(_selected) != clamped) + || (clamped < 0 && !v::is_null(_selected)); + if (clamped >= 0) { + updateSelected(RowSelection{ clamped }); } else { updateSelected({}); } - if (selected >= 0 && selected < limit - && base::ScreenReaderState::Instance()->active()) { - accessibilityChildNameChanged(selected); - accessibilityChildFocused(selected); + const auto shouldAnnounce = (announce == Announce::Always) + || (announce == Announce::OnChange && changed); + if (shouldAnnounce && clamped >= 0) { + accessibilityChildNameChanged(clamped); + accessibilityChildFocused(clamped); } } @@ -1064,6 +1013,84 @@ void Rows::paintEvent(QPaintEvent *e) { } } +QAccessible::Role Rows::accessibilityRole() { + return QAccessible::List; +} + +Qt::FocusPolicy Rows::accessibilityFocusPolicy() { + return Qt::TabFocus; +} + +QAccessible::Role Rows::accessibilityChildRole() const { + return QAccessible::RadioButton; +} + +QAccessible::State Rows::accessibilityChildState(int index) const { + QAccessible::State state; + if (Ui::ScreenReaderModeActive()) { + state.focusable = true; + } + state.checkable = true; + state.checked = (index == chosenIndex()); + if (index == selected()) { + state.active = true; + if (hasFocus()) { + state.focused = true; + } + } + return state; +} + +int Rows::accessibilityChildCount() const { + return count(); +} + +QString Rows::accessibilityChildName(int index) const { + if (index < 0 || index >= count()) { + return {}; + } + const auto &row = rowByIndex(index); + return row.data.nativeName + u", "_q + row.data.name; +} + +QRect Rows::accessibilityChildRect(int index) const { + if (index < 0 || index >= count()) { + return {}; + } + const auto &row = rowByIndex(index); + return QRect(0, row.top, width(), row.height); +} + +int Rows::accessibilityChildColumnCount(int row) const { + return 2; +} + +QAccessible::Role Rows::accessibilityChildSubItemRole() const { + return QAccessible::Cell; +} + +QString Rows::accessibilityChildSubItemName(int row, int column) const { + 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 Rows::accessibilityChildSubItemValue(int row, int column) const { + 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 {}; +} + Content::Content( QWidget *parent, const Languages &recent,