Accessibility fixs:

* Fix focus and screen reader on Dialog #LINQT-2197
* Fix dialer accessibility #LINQT-2204
* Add accessibility to voicemail button #LINQT-2200
*Add list annoncement on magicsearch suggestions #LINQT-2205
* Fix accessibility on contact lists #LINQT-2206
* Fix screen reader does not say values on combobox #LINQT-2195
* Fix focus when close modal #LINQT-2220
* Focus end call button when accepting a call #LINQT-2223
This commit is contained in:
Alexandre Jörgensen 2026-03-17 17:56:12 +01:00
parent 64fedf356d
commit f172435724
21 changed files with 166 additions and 74 deletions

View file

@ -32,12 +32,17 @@ bool FocusNavigator::doesLastFocusWasKeyboard() {
return mLastFocusWasKeyboard;
}
bool FocusNavigator::eventFilter(QObject *, QEvent *event) {
QQuickItem *FocusNavigator::getLastFocusItem() {
return mLastFocusItem;
}
bool FocusNavigator::eventFilter(QObject *obj, QEvent *event) {
switch (event->type()) {
case QEvent::FocusIn: {
auto fe = static_cast<QFocusEvent *>(event);
if (fe) {
int focusReason = fe->reason();
// qDebug() << "New focus object" << obj << "| reason" << focusReason;
mLastFocusWasKeyboard = (focusReason == Qt::TabFocusReason || focusReason == Qt::BacktabFocusReason);
}
break;
@ -45,12 +50,14 @@ bool FocusNavigator::eventFilter(QObject *, QEvent *event) {
default:
break;
}
return false;
return QObject::eventFilter(obj, event);
}
void FocusNavigator::onFocusObjectChanged(QObject *obj) {
// qDebug() << "New focus object" << obj; // Usefull to debug focus problems
auto item = qobject_cast<QQuickItem *>(obj);
mLastFocusItem = item;
if (!item) return;
emit focusChanged(item, mLastFocusWasKeyboard);
emit lastFocusItemChanged();
}

View file

@ -25,18 +25,22 @@
class FocusNavigator : public QObject {
Q_OBJECT
Q_PROPERTY(QQuickItem* lastFocusItem READ getLastFocusItem NOTIFY lastFocusItemChanged)
public:
explicit FocusNavigator(QObject *parent = nullptr);
Q_INVOKABLE bool doesLastFocusWasKeyboard();
Q_INVOKABLE QQuickItem *getLastFocusItem();
protected:
bool eventFilter(QObject *obj, QEvent *event) override;
signals:
void focusChanged(QQuickItem *item, bool keyboardFocus);
void lastFocusItemChanged();
private:
bool mLastFocusWasKeyboard = false;
QQuickItem *mLastFocusItem = nullptr;
void onFocusObjectChanged(QObject *obj);
};

View file

@ -38,6 +38,18 @@ Control.ComboBox {
property color color: DefaultStyle.grey_100
property color disabledColor: DefaultStyle.grey_200
// Accessibility properties
property string accessibleLabel: ""
Accessible.name: generateAccessibleName()
function generateAccessibleName() {
return currentText ?
//: %1 actual value %2
qsTr("combobox_with_value_accessible_name").arg(accessibleLabel).arg(currentText) :
accessibleLabel
}
onConstantImageSourceChanged: if (constantImageSource)
selectedItemImg.imageSource = constantImageSource
onCurrentIndexChanged: {
@ -52,6 +64,12 @@ Control.ComboBox {
selectedItemImg.imageSource = constantImageSource ? constantImageSource : item.img ? item.img : "";
}
// Used for accessibility, contentItem remove value announcement from screen reader
onCurrentTextChanged: {
Accessible.name = generateAccessibleName()
Accessible.announce(currentText)
}
Keys.onPressed: event => {
if (!mainItem.contentItem.activeFocus && (event.key == Qt.Key_Space || event.key == Qt.Key_Enter || event.key == Qt.Key_Return)) {
mainItem.popup.open();
@ -181,11 +199,13 @@ Control.ComboBox {
}
delegate: Item {
id: delegate
width: mainItem.width
height: mainItem.height
// anchors.left: listView.left
// anchors.right: listView.right
Accessible.name: typeof (modelData) != "undefined" ? mainItem.textRole ? modelData[mainItem.textRole] : modelData.text ? modelData.text : modelData : $modelData ? mainItem.textRole ? $modelData[mainItem.textRole] : $modelData : ""
property string valueText: typeof (modelData) != "undefined" ? mainItem.textRole ? modelData[mainItem.textRole] : modelData.text ? modelData.text : modelData : $modelData ? mainItem.textRole ? $modelData[mainItem.textRole] : $modelData : ""
Accessible.name: valueText
RowLayout {
anchors.fill: parent
EffectImage {
@ -216,7 +236,7 @@ Control.ComboBox {
Layout.leftMargin: delegateImg.visible ? 0 : Utils.getSizeWithScreenRatio(flagItem.visble ? 5 : 25)
Layout.rightMargin: Utils.getSizeWithScreenRatio(20)
Layout.alignment: Qt.AlignCenter
text: typeof (modelData) != "undefined" ? mainItem.textRole ? modelData[mainItem.textRole] : modelData.text ? modelData.text : modelData : $modelData ? mainItem.textRole ? $modelData[mainItem.textRole] : $modelData : ""
text: delegate.valueText
elide: Text.ElideRight
maximumLineCount: 1
wrapMode: Text.WrapAnywhere

View file

@ -43,6 +43,7 @@ FocusScope {
signal contactDeletionRequested(FriendGui contact)
signal containsMouseChanged(bool containsMouse)
Accessible.name: displayName
Accessible.role: Accessible.ListItem
MouseArea {
Text {

View file

@ -62,6 +62,9 @@ ListView {
onYChanged: updatePosition()
Accessible.role: Accessible.List
Accessible.name: title
// Qt bug: sometimes, containsMouse may not be send and update on each MouseArea.
// So we need to use this variable to switch off all hovered items.
property int lastMouseContainsIndex: -1

View file

@ -5,30 +5,29 @@ import QtQuick.Controls.Basic
import Linphone
import UtilsCpp
import SettingsCpp
import 'qrc:/qt/qml/Linphone/view/Style/buttonStyle.js' as ButtonStyle
import "qrc:/qt/qml/Linphone/view/Control/Tool/Helper/utils.js" as Utils
Rectangle{
id: mainItem
property int voicemailCount: 0
property bool showMwi: false
property real scaleFactor: 1.0
width: Utils.getSizeWithScreenRatio(42 * scaleFactor)
height: Utils.getSizeWithScreenRatio(36 * scaleFactor)
property real scaleFactor: 1.0
signal clicked()
color: 'transparent'
Button {
IconButton {
anchors.bottom: parent.bottom
anchors.left: parent.left
style: ButtonStyle.noBackground
icon.source: AppIcons.voicemail
icon.color: DefaultStyle.main2_600
width: Utils.getSizeWithScreenRatio(33 * scaleFactor)
height: width
icon.width: width
icon.height: width
padding: 0
background: Item {
anchors.fill: parent
}
//: "Voicemail"
Accessible.name: qsTr("voicemail_accessible_name")
onClicked: {
mainItem.clicked()
}

View file

@ -57,7 +57,7 @@ ColumnLayout {
propertyOwner: SettingsCpp
textRole: 'display_name'
//: Choose %1
Accessible.name: qsTr("choose_something_accessible_name").arg(qsTr("multimedia_settings_ringer_title"))
accessibleLabel: qsTr("choose_something_accessible_name").arg(qsTr("multimedia_settings_ringer_title"))
}
Item {
Layout.fillHeight: true
@ -98,7 +98,7 @@ ColumnLayout {
SettingsCpp.lSetPlaybackDevice(outputAudioDeviceCBox.currentValue)
}
}
Accessible.name: qsTr("choose_something_accessible_name").arg(qsTr("multimedia_settings_speaker_title"))
accessibleLabel: qsTr("choose_something_accessible_name").arg(qsTr("multimedia_settings_speaker_title"))
}
Slider {
id: speakerVolume
@ -149,7 +149,7 @@ ColumnLayout {
SettingsCpp.lSetCaptureDevice(inputAudioDeviceCBox.currentValue)
}
}
Accessible.name: qsTr("choose_something_accessible_name").arg(qsTr("multimedia_settings_microphone_title"))
accessibleLabel: qsTr("choose_something_accessible_name").arg(qsTr("multimedia_settings_microphone_title"))
}
Slider {
id: microVolume
@ -238,7 +238,7 @@ ColumnLayout {
SettingsCpp.lSetVideoDevice(videoDevicesCbox.currentValue)
}
}
Accessible.name: qsTr("choose_something_accessible_name").arg(qsTr("multimedia_settings_camera_title"))
accessibleLabel: qsTr("choose_something_accessible_name").arg(qsTr("multimedia_settings_camera_title"))
}
}
Connections {

View file

@ -1,6 +1,6 @@
import QtQuick
import QtQuick.Controls.Basic as Control
import QtQuick.Layouts as Layout
import QtQuick.Layouts
import QtQuick.Effects
import Linphone
import UtilsCpp
@ -107,11 +107,12 @@ FocusScope {
}
}
Layout.GridLayout {
GridLayout {
id: numPadGrid
columns: 3
columnSpacing: Utils.getSizeWithScreenRatio(40)
rowSpacing: Utils.getSizeWithScreenRatio(10)
function getButtonAt(index){
index = (index+15) % 15
if(index >= 0){
@ -131,7 +132,7 @@ FocusScope {
model: 9
BigButton {
id: numPadButton
Layout.Layout.alignment: Qt.AlignHCenter
Layout.alignment: Qt.AlignHCenter
required property int index
implicitWidth: Utils.getSizeWithScreenRatio(60)
implicitHeight: Utils.getSizeWithScreenRatio(60)
@ -158,7 +159,7 @@ FocusScope {
]
BigButton {
id: digitButton
Layout.Layout.alignment: Qt.AlignHCenter
Layout.alignment: Qt.AlignHCenter
implicitWidth: Utils.getSizeWithScreenRatio(60)
implicitHeight: Utils.getSizeWithScreenRatio(60)
@ -212,7 +213,7 @@ FocusScope {
visible: mainItem.lastRowVisible
implicitWidth: Utils.getSizeWithScreenRatio(75)
implicitHeight: Utils.getSizeWithScreenRatio(55)
Layout.Layout.alignment: Qt.AlignHCenter
Layout.alignment: Qt.AlignHCenter
icon.width: Utils.getSizeWithScreenRatio(32)
icon.height: Utils.getSizeWithScreenRatio(32)
radius: Utils.getSizeWithScreenRatio(71)
@ -231,17 +232,14 @@ FocusScope {
Button {
id: eraseButton
visible: mainItem.lastRowVisible
leftPadding: Utils.getSizeWithScreenRatio(5)
rightPadding: Utils.getSizeWithScreenRatio(5)
topPadding: Utils.getSizeWithScreenRatio(5)
bottomPadding: Utils.getSizeWithScreenRatio(5)
Layout.Layout.alignment: Qt.AlignHCenter
padding: Utils.getSizeWithScreenRatio(5)
Layout.alignment: Qt.AlignHCenter
icon.source: AppIcons.backspaceFill
style: ButtonStyle.noBackground
icon.width: Utils.getSizeWithScreenRatio(38)
icon.height: Utils.getSizeWithScreenRatio(38)
Layout.Layout.preferredWidth: Utils.getSizeWithScreenRatio(38)
Layout.Layout.preferredHeight: Utils.getSizeWithScreenRatio(38)
Layout.preferredWidth: Utils.getSizeWithScreenRatio(38)
Layout.preferredHeight: Utils.getSizeWithScreenRatio(38)
//: Erase
Accessible.name: qsTr("erase_accessible_name")

View file

@ -37,13 +37,38 @@ Popup {
signal accepted()
signal rejected()
property Item itemToFocusOnClose: null
property bool quitWithKeyboard : false
function updateQuitWithKeyboardPress (event) {
if(visible && (event.key == Qt.Key_Escape || event.key == Qt.Key_Enter || event.key == Qt.Key_Space || event.key == Qt.Key_Return)){
mainItem.quitWithKeyboard = true
}
}
onAboutToHide: {
if(mainItem.itemToFocusOnClose && mainItem.itemToFocusOnClose.visible && mainItem.quitWithKeyboard){
// Focus last element that was focused before Dialog was opened
mainItem.itemToFocusOnClose.forceActiveFocus(Qt.TabFocusReason)
}
mainItem.itemToFocusOnClose = null
}
contentItem: FocusScope {
Accessible.role: Accessible.Dialog
Accessible.name: mainItem.title
Accessible.description: mainItem.details
implicitWidth: child.implicitWidth
implicitHeight: child.implicitHeight
onVisibleChanged: {
if(visible) forceActiveFocus()
if(visible){
mainItem.itemToFocusOnClose = FocusNavigator.lastFocusItem
const focusReason = FocusNavigator.doesLastFocusWasKeyboard() ? Qt.TabFocusReason : Qt.OtherFocusReason
forceActiveFocus(focusReason)
}
}
Keys.onPressed: (event) => {
mainItem.updateQuitWithKeyboardPress(event)
if(visible && event.key == Qt.Key_Escape){
mainItem.close()
event.accepted = true
@ -129,6 +154,9 @@ Popup {
mainItem.rejected()
mainItem.close()
}
Keys.onPressed: (event) => {
mainItem.updateQuitWithKeyboardPress(event)
}
KeyNavigation.left: secondButtonId
KeyNavigation.right: secondButtonId
}
@ -145,6 +173,9 @@ Popup {
mainItem.rejected()
mainItem.close()
}
Keys.onPressed: (event) => {
mainItem.updateQuitWithKeyboardPress(event)
}
KeyNavigation.left: firstButtonId
KeyNavigation.right: firstButtonId
}

View file

@ -1,6 +1,6 @@
import QtQuick
import QtQuick.Controls.Basic as Control
import QtQuick.Layouts as Layout
import QtQuick.Layouts
import QtQuick.Effects
import Linphone
import UtilsCpp
@ -10,15 +10,16 @@ import "qrc:/qt/qml/Linphone/view/Control/Tool/Helper/utils.js" as Utils
Control.Popup {
id: mainItem
closePolicy: Control.Popup.CloseOnEscape
leftPadding: Utils.getSizeWithScreenRatio(72)
rightPadding: Utils.getSizeWithScreenRatio(72)
topPadding: Utils.getSizeWithScreenRatio(41)
bottomPadding: Utils.getSizeWithScreenRatio(18)
padding: Utils.getSizeWithScreenRatio(10)
property bool closeButtonVisible: true
property bool roundedBottom: false
property bool lastRowVisible: true
property var currentCall
onOpened: numPad.forceActiveFocus()
focus: true
onOpened: {
const focusReason = FocusNavigator.doesLastFocusWasKeyboard() ? Qt.TabFocusReason : Qt.OtherFocusReason
numPad.forceActiveFocus(focusReason)
}
signal buttonPressed(string text)
signal keyPadKeyPressed(KeyEvent event)
onKeyPadKeyPressed: (event) => {
@ -35,6 +36,8 @@ Control.Popup {
height: parent.height
color: DefaultStyle.grey_100
radius: Utils.getSizeWithScreenRatio(20)
bottomLeftRadius: mainItem.roundedBottom ? radius : 0
bottomRightRadius: mainItem.roundedBottom ? radius : 0
}
MultiEffect {
id: effect
@ -46,24 +49,19 @@ Control.Popup {
shadowBlur: 0.1
z: -1
}
Rectangle {
width: parent.width
height: parent.height / 2
anchors.bottom: parent.bottom
color: DefaultStyle.grey_100
visible: !mainItem.roundedBottom
}
MouseArea {
anchors.fill: parent
onClicked: numPad.forceActiveFocus()
}
}
contentItem: ColumnLayout{
Accessible.role: Accessible.Dialog
//: "Numeric Pad"
Accessible.name : qsTr("numeric_pad_accessible_name")
BigButton {
id: closeButton
visible: mainItem.closeButtonVisible
anchors.top: parent.top
anchors.right: parent.right
anchors.topMargin: Utils.getSizeWithScreenRatio(10)
anchors.rightMargin: Utils.getSizeWithScreenRatio(10)
Layout.alignment: Qt.AlignRight
icon.source: AppIcons.closeX
icon.width: Utils.getSizeWithScreenRatio(24)
icon.height: Utils.getSizeWithScreenRatio(24)
@ -72,9 +70,10 @@ Control.Popup {
//: Close numeric pad
Accessible.name: qsTr("close_numeric_pad_accessible_name")
}
}
contentItem: NumericPad{
NumericPad{
id: numPad
Layout.alignment: Qt.AlignCenter
Layout.bottomMargin: Utils.getSizeWithScreenRatio(5)
lastRowVisible: mainItem.lastRowVisible
currentCall: mainItem.currentCall
onButtonPressed: (text) => {
@ -84,3 +83,4 @@ Control.Popup {
onWipe: mainItem.wipe()
}
}
}

View file

@ -276,7 +276,7 @@ LoginLayout {
})
KeyNavigation.up: displayName
KeyNavigation.down: outboundProxyUriEdit
Accessible.name: qsTr("transport")
accessibleLabel: qsTr("transport")
}
}
}

View file

@ -120,6 +120,8 @@ FocusScope {
startHour.selectedDateTime = UtilsCpp.createDateTime(selectedDate, startHour.selectedHour, startHour.selectedMin)
endHour.selectedDateTime = UtilsCpp.createDateTime(selectedDate, endHour.selectedHour, endHour.selectedMin)
}
//: Day
accessibleLabel: qsTr("day_accessible_name")
}
},
RowLayout {
@ -147,6 +149,8 @@ FocusScope {
endHour.selectedDateTime = UtilsCpp.addSecs(selectedDateTime, 3600)
}
}
//: Start time
accessibleLabel: qsTr("start_time_accessible_name")
}
TimeComboBox {
id: endHour
@ -160,6 +164,8 @@ FocusScope {
KeyNavigation.down: timeZoneCbox
KeyNavigation.left: startHour
KeyNavigation.right: startHour
//: End time
accessibleLabel: qsTr("end_time_accessible_name")
}
Item {
Layout.fillWidth: true
@ -200,6 +206,8 @@ FocusScope {
var modelIndex = timeZoneCbox.model.index(currentIndex, 0)
mainItem.conferenceInfoGui.core.timeZoneModel = timeZoneCbox.model.data(modelIndex, Qt.DisplayRole + 1)
}
//: Timezone
accessibleLabel: qsTr("timezone_accessible_name")
}
]

View file

@ -155,7 +155,7 @@ LoginLayout {
Layout.preferredHeight: Utils.getSizeWithScreenRatio(49)
enabled: false
model: [{text:"@sip.linphone.org"}]
Accessible.name: qsTr("domain")
accessibleLabel: qsTr("domain")
}
EffectImage {
Layout.preferredWidth: Utils.getSizeWithScreenRatio(16)

View file

@ -314,6 +314,8 @@ Item {
sectionsSpacing: Utils.getSizeWithScreenRatio(5)
searchBarText: magicSearchBar.text
//: "Searchbar suggestions"
Accessible.name: qsTr("searchbar_suggestions_accessible_name")
}
}
}
@ -724,7 +726,12 @@ Item {
Component {
id: accountSettingsPageComponent
AccountSettingsPage {
onGoBack: closeContextualMenuComponent()
onGoBack: {
closeContextualMenuComponent()
if(FocusNavigator.doesLastFocusWasKeyboard()){
mainItem.nextItemInFocusChain().forceActiveFocus(Qt.TabFocusReason)
}
}
onAccountRemoved: {
closeContextualMenuComponent();
mainItem.accountRemoved();
@ -734,13 +741,23 @@ Item {
Component {
id: settingsPageComponent
SettingsPage {
onGoBack: closeContextualMenuComponent()
onGoBack: {
closeContextualMenuComponent()
if(FocusNavigator.doesLastFocusWasKeyboard()){
mainItem.nextItemInFocusChain().forceActiveFocus(Qt.TabFocusReason)
}
}
}
}
Component {
id: helpPageComponent
HelpPage {
onGoBack: closeContextualMenuComponent()
onGoBack: {
closeContextualMenuComponent()
if(FocusNavigator.doesLastFocusWasKeyboard()){
mainItem.nextItemInFocusChain().forceActiveFocus(Qt.TabFocusReason)
}
}
}
}
Control.StackView {

View file

@ -176,6 +176,7 @@ AbstractSettingsLayout {
propertyOwnerGui: account
textRole: 'text'
flagRole: 'flag'
accessibleLabel: qsTr("manage_account_international_prefix")
}
SwitchSetting {
titleText: account?.core.humaneReadableRegistrationState

View file

@ -125,7 +125,7 @@ AbstractSettingsLayout {
propertyName: "mediaEncryption"
textRole: 'display_name'
propertyOwner: SettingsCpp
Accessible.name: qsTr("settings_advanced_media_encryption_title")
accessibleLabel: qsTr("settings_advanced_media_encryption_title")
}
}
SwitchSetting {

View file

@ -94,7 +94,7 @@ AbstractSettingsLayout {
SettingsCpp.callForwardToAddress = "voicemail";
}
}
accessibleLabel: qsTr("settings_call_forward_destination_choose")
}
DecoratedTextField {
id: sipInputField

View file

@ -55,6 +55,7 @@ AbstractSettingsLayout {
propertyName: "conferenceLayout"
propertyOwner: SettingsCpp
textRole: 'display_name'
accessibleLabel: qsTr("settings_meetings_default_layout_title")
}
}
SwitchSetting {

View file

@ -337,7 +337,10 @@ AbstractMainPage {
width: parent?.width
height: parent?.height
Control.StackView.onActivated: {
callContactsList.forceActiveFocus()
if(!numericPadPopupItem.visible){
const focusReason = FocusNavigator.doesLastFocusWasKeyboard() ? Qt.TabFocusReason : Qt.OtherFocusReason
callContactsList.forceActiveFocus(focusReason)
}
}
ColumnLayout {
anchors.fill: parent
@ -379,7 +382,6 @@ AbstractMainPage {
Layout.topMargin: Utils.getSizeWithScreenRatio(18)
Layout.fillWidth: true
Layout.fillHeight: true
focus: true
numPadPopup: numericPadPopupItem
searchBarColor: DefaultStyle.grey_100
onContactClicked: contact => {

View file

@ -42,7 +42,7 @@ AbstractMainPage {
rightPanelStackView.clear()
contactList.resetSelections()
}
function goToContactDetails() {
function goToContactDetails(focusEditButton=false) {
if (selectedContact) {
var firstItem = rightPanelStackView.get(0)
if (firstItem && firstItem.objectName == "contactDetail")
@ -58,6 +58,10 @@ AbstractMainPage {
rightPanelStackView.push(contactDetail)
}
}
if(focusEditButton){
rightPanelStackView.get(0).focusEditButton()
}
} else {
rightPanelStackView.clear()
}
@ -335,6 +339,10 @@ AbstractMainPage {
width: parent?.width
height: parent?.height
property string objectName: "contactDetail"
function focusEditButton(){
contactDetail.button.forceActiveFocus(Qt.TabFocusReason)
}
component ContactDetailLayout: ColumnLayout {
id: contactDetailLayout
spacing: Utils.getSizeWithScreenRatio(15)
@ -922,7 +930,7 @@ AbstractMainPage {
ContactEdition {
property string objectName: "contactEdition"
onCloseEdition: redirectAddress => {
goToContactDetails()
goToContactDetails(true)
if (redirectAddress) {
initialFriendToDisplay = redirectAddress
}

View file

@ -333,7 +333,6 @@ AbstractWindow {
Rectangle {
anchors.fill: parent
color: DefaultStyle.grey_900
focus: true
ColumnLayout {
anchors.fill: parent
@ -865,10 +864,6 @@ AbstractWindow {
roundedBottom: true
lastRowVisible: false
visible: false
leftPadding: Utils.getSizeWithScreenRatio(40)
rightPadding: Utils.getSizeWithScreenRatio(40)
topPadding: Utils.getSizeWithScreenRatio(41)
bottomPadding: Utils.getSizeWithScreenRatio(18)
Component.onCompleted: parent.height = height
}
}
@ -904,10 +899,6 @@ AbstractWindow {
parent: numericPadContainer
roundedBottom: true
visible: newCallForm.searchBar.numericPadButton.checked
leftPadding: Utils.getSizeWithScreenRatio(40)
rightPadding: Utils.getSizeWithScreenRatio(40)
topPadding: Utils.getSizeWithScreenRatio(41)
bottomPadding: Utils.getSizeWithScreenRatio(18)
onLaunchCall: {
rightPanel.visible = false
UtilsCpp.createCall(newCallForm.searchBar.text)
@ -1407,6 +1398,7 @@ AbstractWindow {
// End call button
BigButton {
id: endCallButton
focus: true
Layout.row: 0
icon.width: Utils.getSizeWithScreenRatio(32)
icon.height: Utils.getSizeWithScreenRatio(32)