Merge pull request #12138 from Triplelexx/21669_prompt_keyboard

WL#21669: Allow better keyboard navigation of prompt windows such as Window.prompt()
This commit is contained in:
Seth Alves 2018-01-31 11:19:49 -08:00 committed by GitHub
commit 50e6864988
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 145 additions and 37 deletions

View file

@ -32,6 +32,12 @@ Original.Button {
Tablet.playSound(TabletEnums.ButtonHover);
}
}
onFocusChanged: {
if (focus) {
Tablet.playSound(TabletEnums.ButtonHover);
}
}
onClicked: {
Tablet.playSound(TabletEnums.ButtonClick);
@ -59,6 +65,8 @@ Original.Button {
hifi.buttons.pressedColor[control.color]
} else if (control.hovered) {
hifi.buttons.hoveredColor[control.color]
} else if (!control.hovered && control.focus) {
hifi.buttons.focusedColor[control.color]
} else {
hifi.buttons.colorStart[control.color]
}
@ -73,6 +81,8 @@ Original.Button {
hifi.buttons.pressedColor[control.color]
} else if (control.hovered) {
hifi.buttons.hoveredColor[control.color]
} else if (!control.hovered && control.focus) {
hifi.buttons.focusedColor[control.color]
} else {
hifi.buttons.colorFinish[control.color]
}

View file

@ -31,6 +31,12 @@ Original.Button {
}
}
onFocusChanged: {
if (focus) {
Tablet.playSound(TabletEnums.ButtonHover);
}
}
onClicked: {
Tablet.playSound(TabletEnums.ButtonClick);
}
@ -50,6 +56,8 @@ Original.Button {
hifi.buttons.pressedColor[control.color]
} else if (control.hovered) {
hifi.buttons.hoveredColor[control.color]
} else if (!control.hovered && control.focus) {
hifi.buttons.focusedColor[control.color]
} else {
hifi.buttons.colorStart[control.color]
}
@ -64,6 +72,8 @@ Original.Button {
hifi.buttons.pressedColor[control.color]
} else if (control.hovered) {
hifi.buttons.hoveredColor[control.color]
} else if (!control.hovered && control.focus) {
hifi.buttons.focusedColor[control.color]
} else {
hifi.buttons.colorFinish[control.color]
}

View file

@ -93,7 +93,7 @@ TableView {
size: hifi.fontSizes.tableHeadingIcon
anchors {
left: titleText.right
leftMargin: -hifi.fontSizes.tableHeadingIcon / 3 - (centerHeaderText ? 5 : 0)
leftMargin: -hifi.fontSizes.tableHeadingIcon / 3 - (centerHeaderText ? 15 : 10)
right: parent.right
rightMargin: hifi.dimensions.tablePadding
verticalCenter: titleText.verticalCenter

View file

@ -110,7 +110,17 @@ ModalWindow {
}
});
fileTableView.forceActiveFocus();
focusTimer.start();
}
Timer {
id: focusTimer
interval: 10
running: false
repeat: false
onTriggered: {
fileTableView.contentItem.forceActiveFocus();
}
}
Item {
@ -130,7 +140,9 @@ ModalWindow {
drag.target: root
onClicked: {
d.clearSelection();
frame.forceActiveFocus(); // Defocus text field so that the keyboard gets hidden.
// Defocus text field so that the keyboard gets hidden.
// Clicking also breaks keyboard navigation apart from backtabbing to cancel
frame.forceActiveFocus();
}
}
@ -150,6 +162,11 @@ ModalWindow {
size: 30
enabled: fileTableModel.parentFolder && fileTableModel.parentFolder !== ""
onClicked: d.navigateUp();
Keys.onReturnPressed: { d.navigateUp(); }
KeyNavigation.tab: homeButton
KeyNavigation.backtab: upButton
KeyNavigation.left: upButton
KeyNavigation.right: homeButton
}
GlyphButton {
@ -160,6 +177,10 @@ ModalWindow {
width: height
enabled: d.homeDestination ? true : false
onClicked: d.navigateHome();
Keys.onReturnPressed: { d.navigateHome(); }
KeyNavigation.tab: fileTableView.contentItem
KeyNavigation.backtab: upButton
KeyNavigation.left: upButton
}
}
@ -228,9 +249,15 @@ ModalWindow {
d.currentSelectionUrl = helper.pathToUrl(currentText);
}
fileTableModel.folder = folder;
fileTableView.forceActiveFocus();
}
}
KeyNavigation.up: fileTableView.contentItem
KeyNavigation.down: fileTableView.contentItem
KeyNavigation.tab: fileTableView.contentItem
KeyNavigation.backtab: fileTableView.contentItem
KeyNavigation.left: fileTableView.contentItem
KeyNavigation.right: fileTableView.contentItem
}
QtObject {
@ -483,7 +510,6 @@ ModalWindow {
}
headerVisible: !selectDirectory
onDoubleClicked: navigateToRow(row);
focus: true
Keys.onReturnPressed: navigateToCurrentRow();
Keys.onEnterPressed: navigateToCurrentRow();
@ -560,7 +586,7 @@ ModalWindow {
resizable: true
}
TableViewColumn {
id: fileMofifiedColumn
id: fileModifiedColumn
role: "fileModified"
title: "Date"
width: 0.3 * fileTableView.width
@ -571,7 +597,7 @@ ModalWindow {
TableViewColumn {
role: "fileSize"
title: "Size"
width: fileTableView.width - fileNameColumn.width - fileMofifiedColumn.width
width: fileTableView.width - fileNameColumn.width - fileModifiedColumn.width
movable: false
resizable: true
visible: !selectDirectory
@ -649,6 +675,8 @@ ModalWindow {
break;
}
}
KeyNavigation.tab: root.saveDialog ? currentSelection : openButton
}
TextField {
@ -665,6 +693,10 @@ ModalWindow {
activeFocusOnTab: !readOnly
onActiveFocusChanged: if (activeFocus) { selectAll(); }
onAccepted: okAction.trigger();
KeyNavigation.up: fileTableView.contentItem
KeyNavigation.down: openButton
KeyNavigation.tab: openButton
KeyNavigation.backtab: fileTableView.contentItem
}
FileTypeSelection {
@ -675,8 +707,6 @@ ModalWindow {
right: parent.right
}
visible: !selectDirectory && filtersCount > 1
KeyNavigation.left: fileTableView
KeyNavigation.right: openButton
}
Keyboard {
@ -704,18 +734,18 @@ ModalWindow {
color: hifi.buttons.blue
action: okAction
Keys.onReturnPressed: okAction.trigger()
KeyNavigation.up: selectionType
KeyNavigation.left: selectionType
KeyNavigation.right: cancelButton
KeyNavigation.up: root.saveDialog ? currentSelection : fileTableView.contentItem
KeyNavigation.tab: cancelButton
}
Button {
id: cancelButton
action: cancelAction
KeyNavigation.up: selectionType
Keys.onReturnPressed: { cancelAction.trigger() }
KeyNavigation.left: openButton
KeyNavigation.right: fileTableView.contentItem
Keys.onReturnPressed: { canceled(); root.enabled = false }
KeyNavigation.up: root.saveDialog ? currentSelection : fileTableView.contentItem
KeyNavigation.backtab: openButton
}
}

View file

@ -37,6 +37,16 @@ ModalWindow {
return OffscreenUi.waitForMessageBoxResult(root);
}
Keys.onRightPressed: if (defaultButton === OriginalDialogs.StandardButton.Yes) {
yesButton.forceActiveFocus()
} else if (defaultButton === OriginalDialogs.StandardButton.Ok) {
okButton.forceActiveFocus()
}
Keys.onTabPressed: if (defaultButton === OriginalDialogs.StandardButton.Yes) {
yesButton.forceActiveFocus()
} else if (defaultButton === OriginalDialogs.StandardButton.Ok) {
okButton.forceActiveFocus()
}
property alias detailedText: detailedText.text
property alias text: mainTextContainer.text
property alias informativeText: informativeTextContainer.text
@ -47,7 +57,6 @@ ModalWindow {
onIconChanged: updateIcon();
property int defaultButton: OriginalDialogs.StandardButton.NoButton;
property int clickedButton: OriginalDialogs.StandardButton.NoButton;
focus: defaultButton === OriginalDialogs.StandardButton.NoButton
property int titleWidth: 0
onTitleWidthChanged: d.resize();
@ -134,16 +143,35 @@ ModalWindow {
MessageDialogButton { dialog: root; text: qsTr("Reset"); button: OriginalDialogs.StandardButton.Reset; }
MessageDialogButton { dialog: root; text: qsTr("Discard"); button: OriginalDialogs.StandardButton.Discard; }
MessageDialogButton { dialog: root; text: qsTr("No to All"); button: OriginalDialogs.StandardButton.NoToAll; }
MessageDialogButton { dialog: root; text: qsTr("No"); button: OriginalDialogs.StandardButton.No; }
MessageDialogButton {
id: noButton
dialog: root
text: qsTr("No")
button: OriginalDialogs.StandardButton.No
KeyNavigation.left: yesButton
KeyNavigation.backtab: yesButton
}
MessageDialogButton { dialog: root; text: qsTr("Yes to All"); button: OriginalDialogs.StandardButton.YesToAll; }
MessageDialogButton { dialog: root; text: qsTr("Yes"); button: OriginalDialogs.StandardButton.Yes; }
MessageDialogButton {
id: yesButton
dialog: root
text: qsTr("Yes")
button: OriginalDialogs.StandardButton.Yes
KeyNavigation.right: noButton
KeyNavigation.tab: noButton
}
MessageDialogButton { dialog: root; text: qsTr("Apply"); button: OriginalDialogs.StandardButton.Apply; }
MessageDialogButton { dialog: root; text: qsTr("Ignore"); button: OriginalDialogs.StandardButton.Ignore; }
MessageDialogButton { dialog: root; text: qsTr("Retry"); button: OriginalDialogs.StandardButton.Retry; }
MessageDialogButton { dialog: root; text: qsTr("Save All"); button: OriginalDialogs.StandardButton.SaveAll; }
MessageDialogButton { dialog: root; text: qsTr("Save"); button: OriginalDialogs.StandardButton.Save; }
MessageDialogButton { dialog: root; text: qsTr("Open"); button: OriginalDialogs.StandardButton.Open; }
MessageDialogButton { dialog: root; text: qsTr("OK"); button: OriginalDialogs.StandardButton.Ok; }
MessageDialogButton {
id: okButton
dialog: root
text: qsTr("OK")
button: OriginalDialogs.StandardButton.Ok
}
Button {
id: moreButton
@ -230,12 +258,6 @@ ModalWindow {
event.accepted = true
root.click(OriginalDialogs.StandardButton.Cancel)
break
case Qt.Key_Enter:
case Qt.Key_Return:
event.accepted = true
root.click(root.defaultButton)
break
}
}
}

View file

@ -95,19 +95,19 @@ ModalWindow {
TextField {
id: textResult
label: root.label
focus: items ? false : true
visible: items ? false : true
anchors {
left: parent.left;
right: parent.right;
bottom: parent.bottom
}
KeyNavigation.down: acceptButton
KeyNavigation.tab: acceptButton
}
ComboBox {
id: comboBox
label: root.label
focus: true
visible: items ? true : false
anchors {
left: parent.left
@ -115,6 +115,8 @@ ModalWindow {
bottom: parent.bottom
}
model: items ? items : []
KeyNavigation.down: acceptButton
KeyNavigation.tab: acceptButton
}
}
@ -135,7 +137,6 @@ ModalWindow {
Flow {
id: buttons
focus: true
spacing: hifi.dimensions.contentSpacing.x
onHeightChanged: d.resize(); onWidthChanged: d.resize();
layoutDirection: Qt.RightToLeft
@ -145,8 +146,21 @@ ModalWindow {
margins: 0
bottomMargin: hifi.dimensions.contentSpacing.y
}
Button { action: cancelAction }
Button { action: acceptAction }
Button {
id: cancelButton
action: cancelAction
KeyNavigation.left: acceptButton
KeyNavigation.up: items ? comboBox : textResult
KeyNavigation.backtab: acceptButton
}
Button {
id: acceptButton
action: acceptAction
KeyNavigation.right: cancelButton
KeyNavigation.up: items ? comboBox : textResult
KeyNavigation.tab: cancelButton
KeyNavigation.backtab: items ? comboBox : textResult
}
}
Action {
@ -184,7 +198,13 @@ ModalWindow {
case Qt.Key_Return:
case Qt.Key_Enter:
acceptAction.trigger()
if (acceptButton.focus) {
acceptAction.trigger()
} else if (cancelButton.focus) {
cancelAction.trigger()
} else if (comboBox.focus || comboBox.popup.focus) {
comboBox.showList()
}
event.accepted = true;
break;
}
@ -194,6 +214,10 @@ ModalWindow {
keyboardEnabled = HMD.active;
updateIcon();
d.resize();
textResult.forceActiveFocus();
if (items) {
comboBox.forceActiveFocus()
} else {
textResult.forceActiveFocus()
}
}
}

View file

@ -16,10 +16,21 @@ import "../../controls-uit"
Button {
property var dialog;
property int button: StandardButton.NoButton;
property int button: StandardButton.Ok;
color: dialog.defaultButton === button ? hifi.buttons.blue : hifi.buttons.white
focus: dialog.defaultButton === button
color: focus ? hifi.buttons.blue : hifi.buttons.white
onClicked: dialog.click(button)
visible: dialog.buttons & button
Keys.onPressed: {
if (!focus) {
return
}
switch (event.key) {
case Qt.Key_Enter:
case Qt.Key_Return:
event.accepted = true
dialog.click(button)
break
}
}
}

View file

@ -219,6 +219,7 @@ Item {
readonly property var colorFinish: [ colors.lightGrayText, colors.blueAccent, "#94132e", colors.black, Qt.rgba(0, 0, 0, 0), Qt.rgba(0, 0, 0, 0), Qt.rgba(0, 0, 0, 0), Qt.rgba(0, 0, 0, 0) ]
readonly property var hoveredColor: [ colorStart[white], colorStart[blue], colorStart[red], colorFinish[black], colorStart[none], colorStart[noneBorderless], colorStart[noneBorderlessWhite], colorStart[noneBorderlessGray] ]
readonly property var pressedColor: [ colorFinish[white], colorFinish[blue], colorFinish[red], colorStart[black], colorStart[none], colorStart[noneBorderless], colorStart[noneBorderlessWhite], colorStart[noneBorderlessGray] ]
readonly property var focusedColor: [ colors.lightGray50, colors.blueAccent, colors.redAccent, colors.darkGray, colorStart[none], colorStart[noneBorderless], colorStart[noneBorderlessWhite], colorStart[noneBorderlessGray] ]
readonly property var disabledColorStart: [ colorStart[white], colors.baseGrayHighlight]
readonly property var disabledColorFinish: [ colorFinish[white], colors.baseGrayShadow]
readonly property var disabledTextColor: [ colors.lightGrayText, colors.baseGrayShadow]

View file

@ -105,14 +105,14 @@ void WindowScriptingInterface::raiseMainWindow() {
/// \param const QString& message message to display
/// \return QScriptValue::UndefinedValue
void WindowScriptingInterface::alert(const QString& message) {
OffscreenUi::asyncWarning("", message);
OffscreenUi::asyncWarning("", message, QMessageBox::Ok, QMessageBox::Ok);
}
/// Display a confirmation box with the options 'Yes' and 'No'
/// \param const QString& message message to display
/// \return QScriptValue `true` if 'Yes' was clicked, `false` otherwise
QScriptValue WindowScriptingInterface::confirm(const QString& message) {
return QScriptValue((QMessageBox::Yes == OffscreenUi::question("", message, QMessageBox::Yes | QMessageBox::No)));
return QScriptValue((QMessageBox::Yes == OffscreenUi::question("", message, QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes)));
}
/// Display a prompt with a text box
@ -354,7 +354,7 @@ QScriptValue WindowScriptingInterface::browseAssets(const QString& title, const
return result.isEmpty() ? QScriptValue::NullValue : QScriptValue(result);
}
/// Display a select asset dialog that lets the user select an asset from the Asset Server. If `directory` is an invalid
/// Display a select asset dialog that lets the user select an asset from the Asset Server. If `directory` is an invalid
/// directory the browser will start at the root directory.
/// \param const QString& title title of the window
/// \param const QString& directory directory to start the asset browser at