Files
LAIC/venv/Lib/site-packages/PySide6/qml/QtQuick/VirtualKeyboard/Components/Keyboard.qml
T
2026-04-29 07:19:21 +03:00

1858 lines
83 KiB
QML

// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
// Deliberately imported after QtQuick to avoid missing restoreMode property in Binding. Fix in Qt 6.
import QtQml
import QtQuick.Layouts
import QtQuick.Window
import QtQuick.VirtualKeyboard
import QtQuick.VirtualKeyboard.Styles
import QtQuick.VirtualKeyboard.Settings
import QtQuick.VirtualKeyboard.Plugins
import Qt.labs.folderlistmodel
Item {
id: keyboard
objectName: "keyboard"
property alias style: styleLoader.item
property alias wordCandidateView: wordCandidateView
property alias shadowInputControl: shadowInputControl
property alias alternativeKeys: alternativeKeys
property alias characterPreview: characterPreview
property alias wordCandidateContextMenu: wordCandidateContextMenu
property alias fullScreenModeSelectionControl: fullScreenModeSelectionControl
property alias navigationHighlight: navigationHighlight
property alias keyboardInputArea: keyboardInputArea
property Item activeKey: null
property TouchPoint activeTouchPoint
property int localeIndex: -1
property var availableLocaleIndices: []
property var availableCustomLocaleIndices: []
property string locale: localeIndex >= 0 && localeIndex < layoutsModel.count ? layoutsModel.get(localeIndex, "fileName") : ""
property string inputLocale
property int defaultLocaleIndex: -1
readonly property bool latinOnly: InputContext.inputMethodHints & (Qt.ImhLatinOnly | Qt.ImhEmailCharactersOnly | Qt.ImhUrlCharactersOnly)
readonly property bool preferNumbers: InputContext.inputMethodHints & Qt.ImhPreferNumbers
readonly property bool dialableCharactersOnly: InputContext.inputMethodHints & Qt.ImhDialableCharactersOnly
readonly property bool formattedNumbersOnly: InputContext.inputMethodHints & Qt.ImhFormattedNumbersOnly
readonly property bool digitsOnly: InputContext.inputMethodHints & Qt.ImhDigitsOnly
property string layout
property string layoutType: {
if (keyboard.handwritingMode) return "handwriting"
if (keyboard.dialableCharactersOnly) return "dialpad"
if (keyboard.formattedNumbersOnly) return "numbers"
if (keyboard.digitsOnly) return "digits"
if (keyboard.symbolMode) return "symbols"
return "main"
}
property bool active: Qt.inputMethod.visible
property bool handwritingMode
property bool fullScreenHandwritingMode
property bool symbolMode
property bool fullScreenMode: VirtualKeyboardSettings.fullScreenMode
property var defaultInputMethod: initDefaultInputMethod()
property var plainInputMethod: PlainInputMethod {}
property var customInputMethod: null
property var customInputMethodSharedLayouts: []
property int defaultInputMode: InputEngine.InputMode.Latin
property bool inputMethodNeedsReset: true
property bool inputModeNeedsReset: true
property bool navigationModeActive: false
readonly property bool languagePopupListActive: languagePopupList.enabled
property alias soundEffect: soundEffect
property alias keyboardLayoutLoader: keyboardLayoutLoader
property real screenHeight: parent.parent ? parent.parent.height : Screen.height
property bool noAnimations
property int pressAndHoldDelay: 500
function initDefaultInputMethod() {
try {
return Qt.createQmlObject('import QtQuick; import QtQuick.VirtualKeyboard.Plugins; DefaultInputMethod {}', keyboard, "defaultInputMethod")
} catch (e) { }
return plainInputMethod
}
Component.onCompleted: InputContext.priv.registerInputPanel(parent)
width: keyboardBackground.width
height: keyboardBackground.height
onActiveChanged: {
hideLanguagePopup()
if (active && symbolMode && !preferNumbers)
symbolMode = false
keyboardInputArea.reset()
wordCandidateViewAutoHideTimer.stop()
}
onActiveKeyChanged: {
if (InputContext.inputEngine.activeKey !== Qt.Key_unknown)
InputContext.inputEngine.virtualKeyCancel()
}
Connections {
target: VirtualKeyboardSettings
function onLocaleChanged() {
updateDefaultLocale()
localeIndex = defaultLocaleIndex
}
function onActiveLocalesChanged() {
updateDefaultLocale()
if (!isValidLocale(localeIndex) || VirtualKeyboardSettings.locale)
localeIndex = defaultLocaleIndex
}
function onDefaultInputMethodDisabledChanged() {
updateInputMethod()
}
function onArrowKeyNavigationEnabledChanged() {
if (!VirtualKeyboardSettings.arrowKeyNavigationEnabled)
keyboard.navigationModeActive = false
}
}
onAvailableLocaleIndicesChanged: hideLanguagePopup()
onAvailableCustomLocaleIndicesChanged: hideLanguagePopup()
onLocaleChanged: {
hideLanguagePopup()
inputMethodNeedsReset = true
inputModeNeedsReset = true
updateLayout()
}
onInputLocaleChanged: {
if (Qt.locale(inputLocale).name !== "C")
InputContext.priv.locale = inputLocale
}
onLayoutChanged: hideLanguagePopup()
onLayoutTypeChanged: {
updateAvailableLocaleIndices()
updateLayout()
}
onLatinOnlyChanged: inputModeNeedsReset = true
onPreferNumbersChanged: {
keyboard.symbolMode = !keyboard.handwritingMode && preferNumbers
inputModeNeedsReset = true
}
onDialableCharactersOnlyChanged: inputModeNeedsReset = true
onFormattedNumbersOnlyChanged: inputModeNeedsReset = true
onDigitsOnlyChanged: inputModeNeedsReset = true
onHandwritingModeChanged: if (!keyboard.handwritingMode) keyboard.fullScreenHandwritingMode = false
onFullScreenHandwritingModeChanged: if (keyboard.fullScreenHandwritingMode) keyboard.handwritingMode = true
onLanguagePopupListActiveChanged: {
if (languagePopupListActive && navigationModeActive)
keyboardInputArea.initialKey = null
}
Connections {
target: InputContext
function onInputMethodHintsChanged() {
if (InputContext.priv.focus)
updateInputMethod()
}
}
Connections {
target: InputContext.priv
function onInputItemChanged() {
keyboard.hideLanguagePopup()
if (active && symbolMode && !preferNumbers)
symbolMode = false
}
function onFocusChanged() {
if (InputContext.priv.focus)
updateInputMethod()
}
function onNavigationKeyPressed(key, isAutoRepeat) {
var initialKey
var direction = wordCandidateView.effectiveLayoutDirection == Qt.LeftToRight ? 1 : -1
switch (key) {
case Qt.Key_Left:
if (keyboard.navigationModeActive && !keyboardInputArea.initialKey) {
if (languagePopupListActive) {
hideLanguagePopup()
keyboardInputArea.setActiveKey(null)
keyboardInputArea.navigateToNextKey(0, 0, false)
break
}
if (alternativeKeys.active) {
if (alternativeKeys.listView.currentIndex > 0) {
alternativeKeys.listView.decrementCurrentIndex()
} else {
alternativeKeys.close()
keyboardInputArea.setActiveKey(null)
keyboardInputArea.navigateToNextKey(0, 0, false)
}
break
}
if (functionPopupList.active) {
if (functionPopupList.listView.currentIndex > 0) {
functionPopupList.listView.decrementCurrentIndex()
} else {
functionPopupList.close()
keyboardInputArea.setActiveKey(null)
keyboardInputArea.navigateToNextKey(0, 0, false)
}
break
}
if (wordCandidateContextMenu.active) {
hideWordCandidateContextMenu()
break
}
if (wordCandidateView.count) {
if (wordCandidateView.effectiveLayoutDirection == Qt.LeftToRight &&
wordCandidateView.currentIndex > 0) {
wordCandidateView.decrementCurrentIndex()
} else if (wordCandidateView.effectiveLayoutDirection == Qt.RightToLeft &&
wordCandidateView.currentIndex + 1 < wordCandidateView.count) {
wordCandidateView.incrementCurrentIndex()
} else {
keyboardInputArea.navigateToNextKey(0, 0, false)
initialKey = keyboardInputArea.initialKey
while (keyboardInputArea.navigateToNextKey(0, 1 * direction, false))
initialKey = keyboardInputArea.initialKey
while (keyboardInputArea.navigateToNextKey(1, 0, false))
initialKey = keyboardInputArea.initialKey
keyboardInputArea.initialKey = initialKey
keyboardInputArea.navigateToNextKey(0, 0, false)
}
break
}
}
initialKey = keyboardInputArea.initialKey
if (!keyboardInputArea.navigateToNextKey(-1 * direction, 0, false)) {
keyboardInputArea.initialKey = initialKey
if (!keyboardInputArea.navigateToNextKey(0, -1 * direction, false)) {
if (wordCandidateView.count) {
wordCandidateView.currentIndex =
wordCandidateView.effectiveLayoutDirection == Qt.LeftToRight ?
(wordCandidateView.count - 1) : 0
break
}
keyboardInputArea.initialKey = initialKey
keyboardInputArea.navigateToNextKey(0, -1 * direction, true)
}
keyboardInputArea.navigateToNextKey(-1 * direction, 0, true)
}
break
case Qt.Key_Up:
if (languagePopupListActive) {
if (languagePopupList.currentIndex > 0) {
languagePopupList.decrementCurrentIndex()
} else if (languagePopupList.keyNavigationWraps) {
languagePopupList.currentIndex = languagePopupList.count - 1
} else {
hideLanguagePopup()
keyboardInputArea.setActiveKey(null)
keyboardInputArea.navigateToNextKey(0, 0, false)
}
} else if (alternativeKeys.active) {
alternativeKeys.close()
keyboardInputArea.setActiveKey(null)
keyboardInputArea.navigateToNextKey(0, 0, false)
} else if (functionPopupList.active) {
functionPopupList.close()
keyboardInputArea.setActiveKey(null)
keyboardInputArea.navigateToNextKey(0, 0, false)
} else if (wordCandidateContextMenu.active) {
if (wordCandidateContextMenuList.currentIndex > 0) {
wordCandidateContextMenuList.decrementCurrentIndex()
} else if (wordCandidateContextMenuList.keyNavigationWraps && wordCandidateContextMenuList.count > 1) {
wordCandidateContextMenuList.currentIndex = wordCandidateContextMenuList.count - 1
} else {
hideWordCandidateContextMenu()
}
} else if (keyboard.navigationModeActive && !keyboardInputArea.initialKey && wordCandidateView.count) {
keyboardInputArea.navigateToNextKey(0, 0, false)
initialKey = keyboardInputArea.initialKey
if (!keyboardInputArea.navigateToNextKey(0, -1, false)) {
keyboardInputArea.initialKey = initialKey
keyboardInputArea.navigateToNextKey(0, -1, true)
} else {
keyboardInputArea.navigateToNextKey(0, 1, false)
}
} else if (!keyboardInputArea.navigateToNextKey(0, -1, !keyboard.navigationModeActive || !keyboardInputArea.initialKey || wordCandidateView.count == 0)) {
if (wordCandidateView.currentIndex === -1)
wordCandidateView.incrementCurrentIndex()
}
break
case Qt.Key_Right:
if (keyboard.navigationModeActive && !keyboardInputArea.initialKey) {
if (languagePopupListActive) {
hideLanguagePopup()
keyboardInputArea.setActiveKey(null)
keyboardInputArea.navigateToNextKey(0, 0, false)
break
}
if (alternativeKeys.active) {
if (alternativeKeys.listView.currentIndex + 1 < alternativeKeys.listView.count) {
alternativeKeys.listView.incrementCurrentIndex()
} else {
alternativeKeys.close()
keyboardInputArea.setActiveKey(null)
keyboardInputArea.navigateToNextKey(0, 0, false)
}
break
}
if (functionPopupList.active) {
if (functionPopupList.listView.currentIndex + 1 < functionPopupList.listView.count) {
functionPopupList.listView.incrementCurrentIndex()
} else {
functionPopupList.close()
keyboardInputArea.setActiveKey(null)
keyboardInputArea.navigateToNextKey(0, 0, false)
}
break
}
if (wordCandidateContextMenu.active) {
hideWordCandidateContextMenu()
break
}
if (wordCandidateView.count) {
if (wordCandidateView.effectiveLayoutDirection == Qt.LeftToRight &&
wordCandidateView.currentIndex + 1 < wordCandidateView.count) {
wordCandidateView.incrementCurrentIndex()
} else if (wordCandidateView.effectiveLayoutDirection == Qt.RightToLeft &&
wordCandidateView.currentIndex > 0) {
wordCandidateView.decrementCurrentIndex()
} else {
keyboardInputArea.navigateToNextKey(0, 0, false)
initialKey = keyboardInputArea.initialKey
while (keyboardInputArea.navigateToNextKey(0, -1 * direction, false))
initialKey = keyboardInputArea.initialKey;
while (keyboardInputArea.navigateToNextKey(-1, 0, false))
initialKey = keyboardInputArea.initialKey;
keyboardInputArea.initialKey = initialKey
keyboardInputArea.navigateToNextKey(0, 0, false)
}
break
}
}
initialKey = keyboardInputArea.initialKey
if (!keyboardInputArea.navigateToNextKey(1 * direction, 0, false)) {
keyboardInputArea.initialKey = initialKey
if (!keyboardInputArea.navigateToNextKey(0, 1 * direction, false)) {
if (wordCandidateView.count) {
wordCandidateView.currentIndex =
wordCandidateView.effectiveLayoutDirection == Qt.LeftToRight ?
0 : (wordCandidateView.count - 1)
break
}
keyboardInputArea.initialKey = initialKey
keyboardInputArea.navigateToNextKey(0, 1 * direction, true)
}
keyboardInputArea.navigateToNextKey(1 * direction, 0, true)
}
break
case Qt.Key_Down:
if (languagePopupListActive) {
if (languagePopupList.currentIndex + 1 < languagePopupList.count) {
languagePopupList.incrementCurrentIndex()
} else if (languagePopupList.keyNavigationWraps) {
languagePopupList.currentIndex = 0
} else {
hideLanguagePopup()
keyboardInputArea.setActiveKey(null)
keyboardInputArea.navigateToNextKey(0, 0, false)
}
} else if (alternativeKeys.active) {
alternativeKeys.close()
keyboardInputArea.setActiveKey(null)
keyboardInputArea.navigateToNextKey(0, 0, false)
} else if (functionPopupList.active) {
functionPopupList.close()
keyboardInputArea.setActiveKey(null)
keyboardInputArea.navigateToNextKey(0, 0, false)
} else if (wordCandidateContextMenu.active) {
if (wordCandidateContextMenuList.currentIndex + 1 < wordCandidateContextMenuList.count) {
wordCandidateContextMenuList.incrementCurrentIndex()
} else if (wordCandidateContextMenuList.keyNavigationWraps && wordCandidateContextMenuList.count > 1) {
wordCandidateContextMenuList.currentIndex = 0
} else {
hideWordCandidateContextMenu()
keyboardInputArea.setActiveKey(null)
keyboardInputArea.navigateToNextKey(0, 0, false)
}
} else if (keyboard.navigationModeActive && !keyboardInputArea.initialKey && wordCandidateView.count) {
keyboardInputArea.navigateToNextKey(0, 0, false)
initialKey = keyboardInputArea.initialKey
if (!keyboardInputArea.navigateToNextKey(0, 1, false)) {
keyboardInputArea.initialKey = initialKey
keyboardInputArea.navigateToNextKey(0, 1, true)
} else {
keyboardInputArea.navigateToNextKey(0, -1, false)
}
} else if (!keyboardInputArea.navigateToNextKey(0, 1, !keyboard.navigationModeActive || !keyboardInputArea.initialKey || wordCandidateView.count == 0)) {
if (wordCandidateView.currentIndex === -1)
wordCandidateView.incrementCurrentIndex()
}
break
case Qt.Key_Return:
if (!keyboard.navigationModeActive)
break
if (languagePopupListActive) {
if (!isAutoRepeat) {
languagePopupList.model.selectItem(languagePopupList.currentIndex)
keyboardInputArea.reset()
keyboardInputArea.navigateToNextKey(0, 0, false)
}
} else if (keyboardInputArea.initialKey) {
if (!isAutoRepeat) {
pressAndHoldTimer.restart()
keyboardInputArea.setActiveKey(keyboardInputArea.initialKey)
keyboardInputArea.press(keyboardInputArea.initialKey, true)
}
} else if (!wordCandidateContextMenu.active && wordCandidateView.count > 0) {
if (!isAutoRepeat) {
pressAndHoldTimer.restart()
}
}
break
default:
break
}
}
function onNavigationKeyReleased(key, isAutoRepeat) {
switch (key) {
case Qt.Key_Return:
if (!keyboard.navigationModeActive) {
if (languagePopupListActive)
languagePopupList.model.selectItem(languagePopupList.currentIndex)
break
}
if (isAutoRepeat)
break
if (!languagePopupListActive && !alternativeKeys.active && !functionPopupList.active && !wordCandidateContextMenu.active && keyboard.activeKey) {
keyboardInputArea.release(keyboard.activeKey)
pressAndHoldTimer.stop()
alternativeKeys.close()
functionPopupList.close()
keyboardInputArea.setActiveKey(null)
if (!languagePopupListActive && keyboardInputArea.navigationCursor !== Qt.point(-1, -1))
keyboardInputArea.navigateToNextKey(0, 0, false)
} else if (wordCandidateContextMenu.active) {
if (!wordCandidateContextMenu.openedByNavigationKeyLongPress) {
wordCandidateContextMenu.selectCurrentItem()
keyboardInputArea.navigateToNextKey(0, 0, false)
} else {
wordCandidateContextMenu.openedByNavigationKeyLongPress = false
}
} else if (alternativeKeys.active) {
if (!alternativeKeys.openedByNavigationKeyLongPress) {
alternativeKeys.clicked()
alternativeKeys.close()
keyboardInputArea.navigateToNextKey(0, 0, false)
keyboardInputArea.reset()
} else {
alternativeKeys.openedByNavigationKeyLongPress = false
}
} else if (functionPopupList.active) {
if (!functionPopupList.openedByNavigationKeyLongPress) {
functionPopupList.clicked()
functionPopupList.close()
keyboardInputArea.navigateToNextKey(0, 0, false)
keyboardInputArea.reset()
} else {
functionPopupList.openedByNavigationKeyLongPress = false
}
} else if (!wordCandidateContextMenu.active && wordCandidateView.count > 0) {
wordCandidateView.model.selectItem(wordCandidateView.currentIndex)
if (!InputContext.preeditText.length)
keyboardInputArea.navigateToNextKey(0, 1, true)
}
break
default:
break
}
}
}
Connections {
target: InputContext.inputEngine
function onVirtualKeyClicked(key, text, modifiers, isAutoRepeat) {
if (isAutoRepeat && keyboard.activeKey)
soundEffect.play(keyboard.activeKey.soundEffect)
if (key !== Qt.Key_unknown && keyboardInputArea.dragSymbolMode) {
keyboardInputArea.dragSymbolMode = false
keyboard.symbolMode = false
} else if (key === Qt.Key_Space) {
var surroundingText = InputContext.surroundingText.trim()
if (InputContext.priv.shiftHandler.sentenceEndingCharacters.indexOf(surroundingText.charAt(surroundingText.length-1)) >= 0)
keyboard.symbolMode = false
}
}
}
FolderListModel {
id: layoutsModel
nameFilters: ["$"]
folder: VirtualKeyboardSettings.layoutPath
}
Connections {
target: layoutsModel
function onCountChanged() {
updateDefaultLocale()
localeIndex = defaultLocaleIndex
}
}
AlternativeKeys {
id: alternativeKeys
objectName: "alternativeKeys"
// Add some extra margin for decoration
property real horizontalMargin: style.alternateKeysListItemWidth
property real verticalMargin: style.alternateKeysListItemHeight
property rect previewRect: Qt.rect(keyboard.x + alternativeKeys.listView.x - horizontalMargin,
keyboard.y + alternativeKeys.listView.y - verticalMargin,
alternativeKeys.listView.width + horizontalMargin * 2,
alternativeKeys.listView.height + verticalMargin * 2)
property bool openedByNavigationKeyLongPress
onVisibleChanged: {
if (visible)
InputContext.priv.previewRectangle = Qt.binding(function() {return previewRect})
else
openedByNavigationKeyLongPress = false
}
}
FunctionPopupList {
id: functionPopupList
property bool openedByNavigationKeyLongPress
}
Timer {
id: pressAndHoldTimer
interval: keyboard.pressAndHoldDelay
onTriggered: {
if (keyboard.activeKey && keyboard.activeKey === keyboardInputArea.initialKey) {
var origin = keyboard.mapFromItem(activeKey, activeKey.width / 2, 0)
if (keyboard.activeKey.smallText === "\u2699" &&
functionPopupList.open(keyboard.activeKey, origin.x, origin.y)) {
InputContext.inputEngine.virtualKeyCancel()
keyboardInputArea.initialKey = null
functionPopupList.openedByNavigationKeyLongPress = keyboard.navigationModeActive
} else if (alternativeKeys.open(keyboard.activeKey, origin.x, origin.y)) {
InputContext.inputEngine.virtualKeyCancel()
keyboardInputArea.initialKey = null
alternativeKeys.openedByNavigationKeyLongPress = keyboard.navigationModeActive
} else if (keyboard.activeKey.key === Qt.Key_Context1 && !keyboard.symbolMode) {
InputContext.inputEngine.virtualKeyCancel()
keyboardInputArea.dragSymbolMode = true
keyboard.symbolMode = true
keyboardInputArea.initialKey = null
if (keyboardInputArea.navigationCursor !== Qt.point(-1, -1))
keyboardInputArea.navigateToNextKey(0, 0, false)
}
} else if (keyboardInputArea.dragSymbolMode &&
keyboard.activeKey &&
keyboard.activeKey.functionKey &&
!keyboard.activeKey.repeat) {
InputContext.inputEngine.virtualKeyCancel()
keyboardInputArea.click(keyboard.activeKey)
keyboardInputArea.initialKey = null
if (keyboardInputArea.navigationCursor !== Qt.point(-1, -1))
keyboardInputArea.navigateToNextKey(0, 0, false)
} else if (!wordCandidateContextMenu.active && keyboard.navigationModeActive) {
wordCandidateContextMenu.show(wordCandidateView.currentIndex)
wordCandidateContextMenu.openedByNavigationKeyLongPress = keyboard.navigationModeActive
}
}
}
Timer {
id: releaseInaccuracyTimer
interval: 500
onTriggered: {
if (keyboardInputArea.pressed && activeTouchPoint && !alternativeKeys.active && !keyboardInputArea.dragSymbolMode && !functionPopupList.active) {
var key = keyboardInputArea.keyOnPoint(activeTouchPoint.x, activeTouchPoint.y)
if (key !== keyboard.activeKey) {
InputContext.inputEngine.virtualKeyCancel()
keyboardInputArea.setActiveKey(key)
keyboardInputArea.press(key, false)
}
}
}
}
CharacterPreviewBubble {
id: characterPreview
objectName: "characterPreviewBubble"
active: keyboardInputArea.pressed && !alternativeKeys.active && !functionPopupList.active
property rect previewRect: Qt.rect(keyboard.x + characterPreview.x,
keyboard.y + characterPreview.y,
characterPreview.width,
characterPreview.height)
}
Binding {
target: InputContext.priv
property: "previewRectangle"
value: characterPreview.previewRect
when: characterPreview.visible
restoreMode: Binding.RestoreBinding
}
Binding {
target: InputContext.priv
property: "previewRectangle"
value: languagePopupList.previewRect
when: languagePopupListActive
restoreMode: Binding.RestoreBinding
}
Binding {
target: InputContext.priv
property: "previewVisible"
value: characterPreview.visible || languagePopupListActive || alternativeKeys.visible
restoreMode: Binding.RestoreBinding
}
Loader {
id: styleLoader
source: VirtualKeyboardSettings.style
Binding {
target: styleLoader.item
property: "keyboardHeight"
value: keyboardInnerContainer.height
restoreMode: Binding.RestoreBinding
}
}
Loader {
id: navigationHighlight
objectName: "navigationHighlight"
property var highlightItem: {
if (keyboard.navigationModeActive) {
if (languagePopupListActive) {
return languagePopupList.highlightItem
} else if (keyboardInputArea.initialKey) {
return keyboardInputArea.initialKey
} else if (alternativeKeys.listView.count > 0) {
return alternativeKeys.listView.highlightItem
} else if (functionPopupList.listView.count > 0) {
return functionPopupList.listView.highlightItem
} else if (wordCandidateContextMenu.active) {
return wordCandidateContextMenuList.highlightItem
} else if (wordCandidateView.count > 0) {
return wordCandidateView.highlightItem
}
}
return keyboard
}
// Note: without "highlightItem.x - highlightItem.x" the binding does not work for alternativeKeys
property var highlightItemOffset: highlightItem ? keyboard.mapFromItem(highlightItem, highlightItem.x - highlightItem.x, highlightItem.y - highlightItem.y) : ({x:0, y:0})
property int moveDuration: !keyboard.noAnimations ? 200 : 0
property int resizeDuration: !keyboard.noAnimations ? 200 : 0
z: 2
x: highlightItemOffset.x
y: highlightItemOffset.y
width: highlightItem ? highlightItem.width : 0
height: highlightItem ? highlightItem.height : 0
visible: keyboard.navigationModeActive && highlightItem !== null && highlightItem !== keyboard
sourceComponent: keyboard.style.navigationHighlight
Behavior on x {
NumberAnimation { id: xAnimation; duration: navigationHighlight.moveDuration; easing.type: Easing.OutCubic }
}
Behavior on y {
NumberAnimation { id: yAnimation; duration: navigationHighlight.moveDuration; easing.type: Easing.OutCubic }
}
Behavior on width {
NumberAnimation { id: widthAnimation; duration: navigationHighlight.resizeDuration; easing.type: Easing.OutCubic }
}
Behavior on height {
NumberAnimation { id: heightAnimation; duration: navigationHighlight.resizeDuration; easing.type: Easing.OutCubic }
}
}
ShadowInputControl {
id: shadowInputControl
objectName: "shadowInputControl"
z: -3
anchors.left: parent.left
anchors.bottom: wordCandidateView.top
width: Math.min(shadowInputControl.contentWidth, parent.width)
height: shadowInputControl.contentHeight
visible: fullScreenMode && (shadowInputControlVisibleTimer.running || InputContext.animating)
Connections {
target: keyboard
function onActiveChanged() {
if (keyboard.active)
shadowInputControlVisibleTimer.start()
else
shadowInputControlVisibleTimer.stop()
}
}
Timer {
id: shadowInputControlVisibleTimer
interval: 2147483647
repeat: true
}
MouseArea {
onPressed: keyboard.hideLanguagePopup()
anchors.fill: parent
enabled: languagePopupList.enabled
}
}
SelectionControl {
id: fullScreenModeSelectionControl
objectName: "fullScreenModeSelectionControl"
inputContext: InputContext.priv.shadow
anchors.top: shadowInputControl.top
anchors.left: shadowInputControl.left
enabled: keyboard.enabled && fullScreenMode
}
ListView {
id: wordCandidateView
objectName: "wordCandidateView"
clip: true
z: -2
property bool empty: true
readonly property bool visibleCondition: keyboard.active && InputContext.inputEngine.wordCandidateListVisibleHint &&
(!wordCandidateView.empty || wordCandidateViewAutoHideTimer.running)
readonly property bool alwaysVisibleCondition: InputContext.inputEngine.wordCandidateListVisibleHint &&
(keyboard.fullScreenMode || VirtualKeyboardSettings.wordCandidateList.alwaysVisible)
readonly property real visibleYOffset: -height
height: style ? style.selectionListHeight : 0
anchors.left: parent.left
anchors.right: parent.right
spacing: 0
orientation: ListView.Horizontal
snapMode: ListView.SnapToItem
delegate: style.selectionListDelegate
highlight: style.selectionListHighlight ? style.selectionListHighlight : defaultHighlight
highlightMoveDuration: 0
highlightResizeDuration: 0
add: !keyboard.noAnimations ? style.selectionListAdd : null
remove: !keyboard.noAnimations ? style.selectionListRemove : null
keyNavigationWraps: true
model: InputContext.inputEngine.wordCandidateListModel
onCurrentItemChanged: if (currentItem) soundEffect.register(currentItem.soundEffect)
Connections {
target: wordCandidateView.model ? wordCandidateView.model : null
function onActiveItemChanged(index) { wordCandidateView.currentIndex = index }
function onItemSelected() { if (wordCandidateView.currentItem) soundEffect.play(wordCandidateView.currentItem.soundEffect) }
function onCountChanged() {
var empty = wordCandidateView.model.count === 0
if (empty)
wordCandidateViewAutoHideTimer.restart()
else
wordCandidateViewAutoHideTimer.stop()
wordCandidateView.empty = empty
keyboard.hideWordCandidateContextMenu()
}
}
Connections {
target: InputContext.priv
function onInputItemChanged() { wordCandidateViewAutoHideTimer.stop() }
}
Connections {
target: InputContext.inputEngine
function onWordCandidateListVisibleHintChanged() { wordCandidateViewAutoHideTimer.stop() }
}
Timer {
id: wordCandidateViewAutoHideTimer
interval: VirtualKeyboardSettings.wordCandidateList.autoHideDelay
}
Loader {
sourceComponent: style.selectionListBackground
anchors.fill: parent
z: -1
}
Component {
id: defaultHighlight
Item {}
}
states: [
State {
name: "visible"
when: wordCandidateView.visibleCondition
PropertyChanges {
target: wordCandidateView
y: wordCandidateView.visibleYOffset
}
},
State {
name: "alwaysVisible"
when: wordCandidateView.alwaysVisibleCondition
PropertyChanges {
target: wordCandidateView
y: wordCandidateView.visibleYOffset
}
}
]
transitions: Transition {
id: wordCandidateViewTransition
from: ""
to: "visible"
enabled: !InputContext.animating && !keyboard.noAnimations
reversible: true
ParallelAnimation {
NumberAnimation {
properties: "y"
duration: 250
easing.type: Easing.InOutQuad
}
}
}
function longPressItem(index) {
return keyboard.showWordCandidateContextMenu(index)
}
}
Item {
id: soundEffect
property var __sounds: ({})
property bool available: false
signal playingChanged(url source, bool playing)
Connections {
target: VirtualKeyboardSettings
function onStyleNameChanged() {
soundEffect.__sounds = {}
soundEffect.available = false
}
}
function play(sound) {
if (enabled && sound != Qt.resolvedUrl("")) {
var soundId = Qt.md5(sound)
var multiSoundEffect = __sounds[soundId]
if (!multiSoundEffect)
multiSoundEffect = register(sound)
if (multiSoundEffect) {
multiSoundEffect.soundVolume = VirtualKeyboardSettings.convertVolume(VirtualKeyboardSettings.keySoundVolume)
multiSoundEffect.play()
}
}
}
function register(sound) {
var multiSoundEffect = null
if (enabled && sound != Qt.resolvedUrl("")) {
var soundId = Qt.md5(sound)
multiSoundEffect = __sounds[soundId]
if (!multiSoundEffect) {
multiSoundEffect = Qt.createQmlObject('import QtQuick; import QtQuick.VirtualKeyboard; MultiSoundEffect {}', soundEffect)
if (multiSoundEffect) {
multiSoundEffect.playingChanged.connect(soundEffect.playingChanged)
multiSoundEffect.source = sound
__sounds[soundId] = multiSoundEffect
available = true
}
}
}
return multiSoundEffect
}
}
Loader {
id: keyboardBackground
z: -1
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
height: keyboardInnerContainer.height
sourceComponent: style.keyboardBackground
Item {
id: keyboardInnerContainer
readonly property int calculatedWidth: Math.round(keyboardBackground.width)
readonly property int calculatedHeight: Math.round(style.keyboardDesignHeight * calculatedWidth / style.keyboardDesignWidth)
z: 1
width: {
if (style && style.keyboardDesignMaximumHeight > 0 && calculatedHeight > style.keyboardDesignMaximumHeight)
return Math.round(style.keyboardDesignMaximumHeight * style.keyboardDesignWidth / style.keyboardDesignHeight)
return calculatedWidth
}
height: {
if (style) {
if (style.keyboardDesignMaximumHeight > 0 && calculatedHeight > style.keyboardDesignMaximumHeight)
return style.keyboardDesignMaximumHeight
return calculatedHeight
}
return 0
}
anchors.horizontalCenter: parent.horizontalCenter
LayoutMirroring.enabled: false
LayoutMirroring.childrenInherit: true
KeyboardObserver {
id: keyboardObserver
function scanLayout() {
if (keyboardLayoutLoader.item == null)
return null
return keyboardLayoutLoader.item.scanLayout()
}
}
Component.onCompleted: InputContext.priv.setKeyboardObserver(keyboardObserver)
onWidthChanged: notifyLayoutChanged()
onHeightChanged: notifyLayoutChanged()
Loader {
id: keyboardLayoutLoader
objectName: "keyboardLayoutLoader"
anchors.fill: parent
anchors.leftMargin: Math.round(style.keyboardRelativeLeftMargin * parent.width)
anchors.rightMargin: Math.round(style.keyboardRelativeRightMargin * parent.width)
anchors.topMargin: Math.round(style.keyboardRelativeTopMargin * parent.height)
anchors.bottomMargin: Math.round(style.keyboardRelativeBottomMargin * parent.height)
Binding {
target: keyboardLayoutLoader
property: "source"
value: keyboard.layout
when: keyboard.width > 0 && keyboard.layout.length > 0
restoreMode: Binding.RestoreNone
}
onItemChanged: {
if (!item)
return
// Reset input mode if the new layout wants to override it
if (item.inputMode !== -1)
inputModeNeedsReset = true
if (!InputContext.inputEngine.inputMethod)
updateInputMethod()
notifyLayoutChanged()
}
MultiPointTouchArea {
id: keyboardInputArea
objectName: "keyboardInputArea"
property Item initialKey: null
property bool dragSymbolMode
property real releaseMargin: initialKey !== null ? Math.min(initialKey.width / 3, initialKey.height / 3) : 0
property point navigationCursor: Qt.point(-1, -1)
anchors.fill: keyboardLayoutLoader
Connections {
target: keyboardLayoutLoader
function onLoaded() {
if (keyboard.navigationModeActive &&
keyboardInputArea.navigationCursor !== Qt.point(-1, -1))
keyboard.navigationModeActive = keyboardInputArea.navigateToNextKey(0, 0, false)
}
}
Connections {
target: keyboard
function onNavigationModeActiveChanged() {
if (!keyboard.navigationModeActive) {
keyboardInputArea.navigationCursor = Qt.point(-1, -1)
keyboardInputArea.reset()
}
}
}
function press(key, isRealPress) {
if (key && key.enabled) {
if (!key.noKeyEvent)
InputContext.inputEngine.virtualKeyPress(key.key, key.uppercased ? key.text.toUpperCase() : key.text, key.uppercased ? Qt.ShiftModifier : 0, key.repeat && !dragSymbolMode)
if (isRealPress)
soundEffect.play(key.soundEffect)
}
}
function release(key) {
if (key && key.enabled) {
if (!key.noKeyEvent)
InputContext.inputEngine.virtualKeyRelease(key.key, key.uppercased ? key.text.toUpperCase() : key.text, key.uppercased ? Qt.ShiftModifier : 0)
key.clicked()
}
}
function click(key) {
if (key && key.enabled) {
if (!key.noKeyEvent)
InputContext.inputEngine.virtualKeyClick(key.key, InputContext.uppercase ? key.text.toUpperCase() : key.text, InputContext.uppercase ? Qt.ShiftModifier : 0)
key.clicked()
}
}
function setActiveKey(activeKey) {
if (keyboard.activeKey === activeKey)
return
if (keyboard.activeKey) {
if (keyboard.activeKey.keyType === QtVirtualKeyboard.KeyType.FlickKey)
keyboard.activeKey.onKeyChanged.disconnect(onFlickKeyKeyChanged)
keyboard.activeKey.active = false
}
keyboard.activeKey = activeKey
if (keyboard.activeKey) {
keyboard.activeKey.active = true
}
}
function keyOnPoint(px, py) {
var parentItem = keyboardLayoutLoader
var child = parentItem.childAt(px, py)
while (child !== null) {
var position = parentItem.mapToItem(child, px, py)
px = position.x; py = position.y
parentItem = child
child = parentItem.childAt(px, py)
if (child && child.key !== undefined)
return child
}
return null
}
function hitInitialKey(x, y, margin) {
if (!initialKey)
return false
var position = initialKey.mapFromItem(keyboardInputArea, x, y)
return (position.x > -margin
&& position.y > -margin
&& position.x < initialKey.width + margin
&& position.y < initialKey.height + margin)
}
function containsPoint(touchPoints, point) {
if (!point)
return false
for (var i in touchPoints)
if (touchPoints[i].pointId == point.pointId)
return true
return false
}
function releaseActiveKey() {
if (alternativeKeys.active) {
alternativeKeys.clicked()
} else if (functionPopupList.active) {
functionPopupList.clicked()
} else if (keyboard.activeKey) {
release(keyboard.activeKey)
}
reset()
}
function reset() {
releaseInaccuracyTimer.stop()
pressAndHoldTimer.stop()
setActiveKey(null)
activeTouchPoint = null
alternativeKeys.close()
functionPopupList.close()
if (dragSymbolMode) {
keyboard.symbolMode = false
dragSymbolMode = false
}
}
function nextKeyInNavigation(dX, dY, wrapEnabled) {
var nextKey = null, x, y, itemOffset
if (dX !== 0 || dY !== 0) {
var offsetX, offsetY
for (offsetX = dX, offsetY = dY;
Math.abs(offsetX) < width && Math.abs(offsetY) < height;
offsetX += dX, offsetY += dY) {
x = navigationCursor.x + offsetX
if (x < 0) {
if (!wrapEnabled)
break
x += width
} else if (x >= width) {
if (!wrapEnabled)
break
x -= width
}
y = navigationCursor.y + offsetY
if (y < 0) {
if (!wrapEnabled)
break
y += height
} else if (y >= height) {
if (!wrapEnabled)
break
y -= height
}
nextKey = keyOnPoint(x, y)
if (nextKey) {
// Check if key is visible. Only the visible keys have keyPanelDelegate set.
if (nextKey != initialKey && nextKey.hasOwnProperty("keyPanelDelegate") && nextKey.keyPanelDelegate)
break
// Jump over the item to reduce the number of iterations in this loop
itemOffset = mapToItem(nextKey, x, y)
if (dX > 0)
offsetX += nextKey.width - itemOffset.x
else if (dX < 0)
offsetX -= itemOffset.x
else if (dY > 0)
offsetY += nextKey.height - itemOffset.y
else if (dY < 0)
offsetY -= itemOffset.y
}
nextKey = null
}
} else {
nextKey = keyOnPoint(navigationCursor.x, navigationCursor.y)
}
if (nextKey) {
itemOffset = mapFromItem(nextKey, nextKey.width / 2, nextKey.height / 2)
if (dX) {
x = itemOffset.x
} else if (dY) {
y = itemOffset.y
} else {
x = itemOffset.x
y = itemOffset.y
}
navigationCursor = Qt.point(x, y)
}
return nextKey
}
function navigateToNextKey(dX, dY, wrapEnabled) {
// Resolve initial landing point of the navigation cursor
if (!keyboard.navigationModeActive || keyboard.navigationCursor === Qt.point(-1, -1)) {
if (dX > 0)
navigationCursor = Qt.point(0, height / 2)
else if (dX < 0)
navigationCursor = Qt.point(width, height / 2)
else if (dY > 0)
navigationCursor = Qt.point(width / 2, 0)
else if (dY < 0)
navigationCursor = Qt.point(width / 2, height)
else
navigationCursor = Qt.point(width / 2, height / 2)
keyboard.navigationModeActive = true
}
if (dX && dY) {
initialKey = nextKeyInNavigation(dX, 0, wrapEnabled)
if (initialKey || wrapEnabled)
initialKey = nextKeyInNavigation(0, dY, wrapEnabled)
} else {
initialKey = nextKeyInNavigation(dX, dY, wrapEnabled)
}
return initialKey !== null
}
function onFlickKeyKeyChanged() {
InputContext.inputEngine.virtualKeyCancel()
press(activeKey, false)
}
onPressed: (touchPoints) => {
keyboard.navigationModeActive = false
// Immediately release any pending key that the user might be
// holding (and about to release) when a second key is pressed.
if (activeTouchPoint)
releaseActiveKey();
for (var i in touchPoints) {
// Release any key pressed by a previous iteration of the loop.
if (containsPoint(touchPoints, activeTouchPoint))
releaseActiveKey();
initialKey = keyOnPoint(touchPoints[i].x, touchPoints[i].y)
if (!initialKey)
continue
activeTouchPoint = touchPoints[i]
if (initialKey.keyType === QtVirtualKeyboard.KeyType.FlickKey) {
initialKey.press(activeTouchPoint.x, activeTouchPoint.y)
initialKey.onKeyChanged.connect(onFlickKeyKeyChanged)
} else {
releaseInaccuracyTimer.start()
pressAndHoldTimer.start()
}
setActiveKey(initialKey)
press(initialKey, true)
}
}
onUpdated: (touchPoints) => {
if (!containsPoint(touchPoints, activeTouchPoint))
return
if (alternativeKeys.active) {
alternativeKeys.move(mapToItem(alternativeKeys, activeTouchPoint.x, 0).x)
} else if (functionPopupList.active) {
functionPopupList.move(mapToItem(functionPopupList, activeTouchPoint.x, activeTouchPoint.y))
} else if (activeKey && activeKey.keyType === QtVirtualKeyboard.KeyType.FlickKey) {
activeKey.update(activeTouchPoint.x, activeTouchPoint.y)
} else {
var key = null
if (releaseInaccuracyTimer.running) {
if (hitInitialKey(activeTouchPoint.x, activeTouchPoint.y, releaseMargin)) {
key = initialKey
} else if (initialKey) {
releaseInaccuracyTimer.stop()
initialKey = null
}
}
if (key === null) {
key = keyOnPoint(activeTouchPoint.x, activeTouchPoint.y)
}
if (key !== keyboard.activeKey) {
InputContext.inputEngine.virtualKeyCancel()
setActiveKey(key)
press(key, false)
if (dragSymbolMode) {
if (key && key.functionKey && key.key !== Qt.Key_Context1)
pressAndHoldTimer.restart()
else
pressAndHoldTimer.stop()
}
}
}
}
onReleased: (touchPoints) => {
if (containsPoint(touchPoints, activeTouchPoint)) {
if (dragSymbolMode) {
var key = keyOnPoint(activeTouchPoint.x, activeTouchPoint.y)
if (key && key.key === Qt.Key_Context1) {
dragSymbolMode = false
InputContext.inputEngine.virtualKeyCancel()
reset()
return
}
}
releaseActiveKey();
}
}
onCanceled: (touchPoints) => {
if (containsPoint(touchPoints, activeTouchPoint))
reset()
}
}
}
}
}
Item {
id: languagePopup
z: 1
anchors.fill: parent
LayoutMirroring.enabled: false
LayoutMirroring.childrenInherit: true
MouseArea {
onPressed: keyboard.hideLanguagePopup()
anchors.fill: parent
enabled: languagePopupList.enabled
}
PopupList {
id: languagePopupList
objectName: "languagePopupList"
z: 2
anchors.left: parent.left
anchors.top: parent.top
enabled: false
model: languageListModel
delegate: keyboard.style ? keyboard.style.languageListDelegate : null
highlight: keyboard.style ? keyboard.style.languageListHighlight : defaultHighlight
add: keyboard.style && !keyboard.noAnimations ? keyboard.style.languageListAdd : null
remove: keyboard.style && !keyboard.noAnimations ? keyboard.style.languageListRemove : null
property rect previewRect: Qt.rect(keyboard.x + languagePopupList.x,
keyboard.y + languagePopupList.y,
languagePopupList.width,
languagePopupList.height)
}
Loader {
sourceComponent: keyboard.style.languageListBackground
anchors.fill: languagePopupList
z: -1
visible: languagePopupList.visible
}
ListModel {
id: languageListModel
function selectItem(index) {
languagePopupList.currentIndex = index
keyboard.soundEffect.play(languagePopupList.currentItem.soundEffect)
changeLanguageTimer.newLocaleIndex = languageListModel.get(index).localeIndex
changeLanguageTimer.start()
}
}
Timer {
id: changeLanguageTimer
interval: 1
property int newLocaleIndex
onTriggered: {
if (languagePopupListActive) {
hideLanguagePopup()
start()
} else {
localeIndex = newLocaleIndex
}
}
}
function show(locales, parentItem, customLayoutsOnly) {
let currentIndex = -1
if (!languagePopupList.enabled) {
languageListModel.clear()
for (var i = 0; i < locales.length; i++) {
languageListModel.append({localeName: locales[i].name, displayName: locales[i].locale.nativeLanguageName, localeIndex: locales[i].index})
if (locales[i].index === keyboard.localeIndex)
currentIndex = i
}
if (parentItem) {
languagePopupList.anchors.leftMargin = Qt.binding(function() {
const newLeftMargin = Math.round(keyboard.mapFromItem(parentItem, (parentItem.width - languagePopupList.width) / 2, 0).x)
return Math.min(Math.max(0, newLeftMargin), keyboard.width - languagePopupList.width)
})
languagePopupList.anchors.topMargin = Qt.binding(function() {return Math.round(keyboard.mapFromItem(parentItem, 0, -languagePopupList.height).y)})
} else {
languagePopupList.anchors.leftMargin = Qt.binding(function() {return Math.round((keyboard.width - languagePopupList.width) / 2)})
languagePopupList.anchors.topMargin = Qt.binding(function() {return Math.round((keyboard.height - languagePopupList.height) / 2)})
}
}
languagePopupList.enabled = true
if (currentIndex !== -1) {
languagePopupList.currentIndex = currentIndex
languagePopupList.forceLayout()
languagePopupList.positionViewAtIndex(currentIndex, ListView.Center)
}
}
function hide() {
if (languagePopupList.enabled) {
languagePopupList.enabled = false
languagePopupList.anchors.leftMargin = undefined
languagePopupList.anchors.topMargin = undefined
languageListModel.clear()
}
}
}
function showLanguagePopup(parentItem, customLayoutsOnly) {
var locales = keyboard.listLocales(customLayoutsOnly, parent.externalLanguageSwitchEnabled)
if (parent.externalLanguageSwitchEnabled) {
var currentIndex = 0
for (var i = 0; i < locales.length; i++) {
if (locales[i] === keyboard.locale) {
currentIndex = i
break
}
}
parent.externalLanguageSwitch(locales, currentIndex)
return
}
languagePopup.show(locales, parentItem, customLayoutsOnly)
}
function hideLanguagePopup() {
languagePopup.hide()
}
MouseArea {
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
height: keyboard.screenHeight
onPressed: keyboard.hideWordCandidateContextMenu()
enabled: wordCandidateContextMenuList.enabled
}
Item {
id: wordCandidateContextMenu
objectName: "wordCandidateContextMenu"
z: 1
anchors.fill: parent
LayoutMirroring.enabled: false
LayoutMirroring.childrenInherit: true
property int previousWordCandidateIndex: -1
readonly property bool active: wordCandidateContextMenuList.visible
property bool openedByNavigationKeyLongPress
PopupList {
id: wordCandidateContextMenuList
objectName: "wordCandidateContextMenuList"
z: 2
anchors.left: parent.left
anchors.top: parent.top
enabled: false
model: wordCandidateContextMenuListModel
property rect previewRect: Qt.rect(keyboard.x + wordCandidateContextMenuList.x,
keyboard.y + wordCandidateContextMenuList.y,
wordCandidateContextMenuList.width,
wordCandidateContextMenuList.height)
}
Loader {
sourceComponent: keyboard.style.popupListBackground
anchors.fill: wordCandidateContextMenuList
z: -1
visible: wordCandidateContextMenuList.visible
}
ListModel {
id: wordCandidateContextMenuListModel
function selectItem(index) {
wordCandidateContextMenu.previousWordCandidateIndex = -1
wordCandidateContextMenuList.currentIndex = index
keyboard.soundEffect.play(wordCandidateContextMenuList.currentItem.soundEffect)
switch (get(index).action) {
case "remove":
wordCandidateView.model.removeItem(wordCandidateView.currentIndex)
break
}
keyboard.hideWordCandidateContextMenu()
}
}
function show(wordCandidateIndex) {
if (wordCandidateContextMenu.enabled)
wordCandidateContextMenu.hide()
wordCandidateContextMenuListModel.clear()
var canRemoveSuggestion = wordCandidateView.model.dataAt(wordCandidateIndex, SelectionListModel.Role.CanRemoveSuggestion)
if (canRemoveSuggestion) {
var dictionaryType = wordCandidateView.model.dataAt(wordCandidateIndex, SelectionListModel.Role.Dictionary)
var removeItemText;
switch (dictionaryType) {
case SelectionListModel.DictionaryType.User:
//~ VirtualKeyboard Context menu for word suggestion if it can be removed from the user dictionary.
removeItemText = qsTr("Remove from dictionary")
break
case SelectionListModel.DictionaryType.Default:
// Fallthrough
default:
//~ VirtualKeyboard Context menu for word suggestion if it can be removed from the default dictionary.
removeItemText = qsTr("Block word")
break
}
wordCandidateContextMenuListModel.append({action: "remove", display: removeItemText, wordCompletionLength: 0})
}
if (wordCandidateContextMenuListModel.count === 0)
return
previousWordCandidateIndex = wordCandidateView.currentIndex
wordCandidateView.currentIndex = wordCandidateIndex
wordCandidateContextMenuList.anchors.leftMargin = Qt.binding(function() {
if (!wordCandidateView.currentItem)
return 0
var leftBorder = Math.round(wordCandidateView.mapFromItem(wordCandidateView.currentItem, (wordCandidateView.currentItem.width - wordCandidateContextMenuList.width) / 2, 0).x)
var rightBorder = Math.round(wordCandidateContextMenuList.parent.width - wordCandidateContextMenuList.width)
return Math.max(0, Math.min(leftBorder, rightBorder))
})
wordCandidateContextMenuList.enabled = true
wordCandidateContextMenuList.currentIndex = 0
}
function hide() {
if (wordCandidateContextMenuList.enabled) {
if (previousWordCandidateIndex !== -1) {
wordCandidateView.currentIndex = previousWordCandidateIndex
previousWordCandidateIndex = -1
}
wordCandidateContextMenuList.enabled = false
wordCandidateContextMenuList.anchors.leftMargin = undefined
wordCandidateContextMenuListModel.clear()
}
openedByNavigationKeyLongPress = false
}
function selectCurrentItem() {
if (active && wordCandidateContextMenuList.currentIndex !== -1)
wordCandidateContextMenuListModel.selectItem(wordCandidateContextMenuList.currentIndex)
}
}
function showWordCandidateContextMenu(wordCandidateIndex) {
wordCandidateContextMenu.show(wordCandidateIndex)
}
function hideWordCandidateContextMenu() {
wordCandidateContextMenu.hide()
}
function updateInputMethod() {
if (!keyboardLayoutLoader.item)
return
if (!InputContext.priv.focus)
return
// Reset the custom input method if it is not included in the list of shared layouts
if (customInputMethod && !inputMethodNeedsReset && customInputMethodSharedLayouts.indexOf(layoutType) === -1)
inputMethodNeedsReset = true
var customInputMethodToDestroy = null
if (inputMethodNeedsReset) {
if (customInputMethod) {
// Postpones the destruction of the custom input method after creating a new one
// and after assigning it to the input engine. This allows the input method to clear
// its state before destroying.
customInputMethodToDestroy = customInputMethod
customInputMethod = null
}
customInputMethodSharedLayouts = []
inputMethodNeedsReset = false
}
var inputMethod = null
var inputMode = InputContext.inputEngine.inputMode
// Use input method from keyboard layout
if (keyboardLayoutLoader.item.inputMethod) {
inputMethod = keyboardLayoutLoader.item.inputMethod
} else if (!customInputMethod) {
try {
customInputMethod = keyboardLayoutLoader.item.createInputMethod()
if (customInputMethod) {
// Pull the list of shared layouts from the keyboard layout
if (keyboardLayoutLoader.item.sharedLayouts)
customInputMethodSharedLayouts = customInputMethodSharedLayouts.concat(keyboardLayoutLoader.item.sharedLayouts)
// Make sure the current layout is included in the list
if (customInputMethodSharedLayouts.indexOf(layoutType) === -1)
customInputMethodSharedLayouts.push(layoutType)
// Reset input mode, since inputEngine.inputModes is updated
inputModeNeedsReset = true
}
} catch (e) {
console.error(e.message)
}
}
if (!inputMethod) {
if (customInputMethod) {
inputMethod = customInputMethod
} else if (!VirtualKeyboardSettings.defaultInputMethodDisabled) {
inputMethod = defaultInputMethod
} else {
inputMethod = plainInputMethod
}
}
var inputMethodChanged = InputContext.inputEngine.inputMethod !== inputMethod
if (inputMethodChanged) {
InputContext.inputEngine.inputMethod = inputMethod
}
if (InputContext.inputEngine.inputMethod) {
var inputModes = InputContext.inputEngine.inputModes
if (inputModes.length > 0) {
// Reset to default input mode if the input locale has changed
if (inputModeNeedsReset) {
inputMode = inputModes[0]
// Check the current layout for input mode override
if (keyboardLayoutLoader.item.inputMode !== -1)
inputMode = keyboardLayoutLoader.item.inputMode
// Update input mode automatically in handwriting mode
if (keyboard.handwritingMode) {
if (keyboard.dialableCharactersOnly && inputModes.indexOf(InputEngine.InputMode.Dialable) !== -1)
inputMode = InputEngine.InputMode.Dialable
else if ((keyboard.formattedNumbersOnly || keyboard.digitsOnly) && inputModes.indexOf(InputEngine.InputMode.Numeric) !== -1)
inputMode = InputEngine.InputMode.Numeric
else if (keyboardLayoutLoader.item.inputMode === -1)
inputMode = inputModes[0]
}
// Check the input method hints for input mode overrides
if (latinOnly)
inputMode = InputEngine.InputMode.Latin
if (preferNumbers)
inputMode = InputEngine.InputMode.Numeric
}
// Make sure the input mode is supported by the current input method
if (inputModes.indexOf(inputMode) === -1)
inputMode = inputModes[0]
if (InputContext.inputEngine.inputMode !== inputMode || inputMethodChanged || inputModeNeedsReset) {
InputContext.priv.setKeyboardObserver(keyboardObserver)
InputContext.inputEngine.inputMode = inputMode
}
inputModeNeedsReset = false
}
}
if (customInputMethodToDestroy !== null)
customInputMethodToDestroy.destroy()
// Clear the toggle shift timer
InputContext.priv.shiftHandler.clearToggleShiftTimer()
}
function updateLayout() {
var newLayout
newLayout = findLayout(locale, layoutType)
if (!newLayout.length) {
newLayout = findLayout(locale, "main")
}
layout = newLayout
inputLocale = locale
updateInputMethod()
}
function updateDefaultLocale() {
updateAvailableLocaleIndices()
if (layoutsModel.count > 0) {
var defaultLocales = []
if (isValidLocale(VirtualKeyboardSettings.locale))
defaultLocales.push(VirtualKeyboardSettings.locale)
if (isValidLocale(InputContext.locale))
defaultLocales.push(InputContext.locale)
if (VirtualKeyboardSettings.activeLocales.length > 0 && isValidLocale(VirtualKeyboardSettings.activeLocales[0]))
defaultLocales.push(VirtualKeyboardSettings.activeLocales[0])
if (VirtualKeyboardSettings.availableLocales.indexOf("en_GB") !== -1)
defaultLocales.push("en_GB")
if (availableLocaleIndices.length > 0)
defaultLocales.push(layoutsModel.get(availableLocaleIndices[0], "fileName"))
var newDefaultLocaleIndex = -1
for (var i = 0; i < defaultLocales.length; i++) {
newDefaultLocaleIndex = findLocale(defaultLocales[i], -1)
if (availableLocaleIndices.indexOf(newDefaultLocaleIndex) !== -1)
break;
newDefaultLocaleIndex = -1
}
defaultLocaleIndex = newDefaultLocaleIndex
} else {
defaultLocaleIndex = -1
}
}
function filterLocaleIndices(filterCb) {
var localeIndices = []
for (var i = 0; i < layoutsModel.count; i++) {
if (localeIndices.indexOf(i) === -1) {
var localeName = layoutsModel.get(i, "fileName")
if (filterCb(localeName) && findLayout(localeName, "main"))
localeIndices.push(i)
}
}
return localeIndices
}
function updateAvailableLocaleIndices() {
// Update list of all available locales
var fallbackIndex = findFallbackIndex()
var newIndices = filterLocaleIndices(function(localeName) {
return isValidLocale(localeName)
})
// Handle case where the VirtualKeyboardSettings.activeLocales contains no valid entries
// Fetch all locales by ignoring active locales setting
var ignoreActiveLocales = newIndices.length === 0
if (ignoreActiveLocales) {
newIndices = filterLocaleIndices(function(localeName) {
return isValidLocale(localeName, ignoreActiveLocales)
})
}
// Fetch matching locale names
var newAvailableLocales = []
for (var i = 0; i < newIndices.length; i++) {
newAvailableLocales.push(layoutsModel.get(newIndices[i], "fileName"))
}
newAvailableLocales.sort()
var sortOrder = !ignoreActiveLocales && VirtualKeyboardSettings.activeLocales.length > 0 ?
VirtualKeyboardSettings.activeLocales :
newAvailableLocales
newIndices.sort(function(localeIndexA, localeIndexB) {
var localeNameA = layoutsModel.get(localeIndexA, "fileName")
var localeNameB = layoutsModel.get(localeIndexB, "fileName")
var sortIndexA = sortOrder.indexOf(localeNameA)
var sortIndexB = sortOrder.indexOf(localeNameB)
return sortIndexA - sortIndexB
})
availableLocaleIndices = newIndices
InputContext.priv.updateAvailableLocales(newAvailableLocales)
// Update list of custom locale indices
newIndices = []
for (i = 0; i < availableLocaleIndices.length; i++) {
if (availableLocaleIndices[i] === localeIndex ||
layoutExists(layoutsModel.get(availableLocaleIndices[i], "fileName"), layoutType))
newIndices.push(availableLocaleIndices[i])
}
availableCustomLocaleIndices = newIndices
}
function listLocales(customLayoutsOnly, localeNameOnly) {
var locales = []
var localeIndices = customLayoutsOnly ? availableCustomLocaleIndices : availableLocaleIndices
for (var i = 0; i < localeIndices.length; i++) {
var layoutFolder = layoutsModel.get(localeIndices[i], "fileName")
if (localeNameOnly)
locales.push(layoutFolder)
else
locales.push({locale:Qt.locale(layoutFolder), index:localeIndices[i], name:layoutFolder})
}
return locales
}
function nextLocaleIndex(customLayoutsOnly) {
var newLocaleIndex = localeIndex
var localeIndices = customLayoutsOnly ? availableCustomLocaleIndices : availableLocaleIndices
var i = localeIndices.indexOf(localeIndex)
if (i !== -1) {
i = (i + 1) % localeIndices.length
newLocaleIndex = localeIndices[i]
}
return newLocaleIndex
}
function changeInputLanguage(customLayoutsOnly) {
var newLocaleIndex = nextLocaleIndex(customLayoutsOnly)
if (newLocaleIndex !== -1 && newLocaleIndex !== localeIndex)
localeIndex = newLocaleIndex
}
function canChangeInputLanguage(customLayoutsOnly) {
if (customLayoutsOnly)
return availableCustomLocaleIndices.length > 1
return availableLocaleIndices.length > 1
}
function findLocale(localeName, defaultValue) {
var languageCode = localeName.substring(0, 3) // Including the '_' delimiter
var languageMatch = -1
for (var i = 0; i < layoutsModel.count; i++) {
if (!layoutsModel.isFolder(i))
continue
var layoutFolder = layoutsModel.get(i, "fileName")
if (layoutFolder === localeName)
return i
if (languageMatch == -1 && layoutFolder.substring(0, 3) === languageCode)
languageMatch = i
}
return (languageMatch != -1) ? languageMatch : defaultValue
}
function findFallbackIndex() {
for (var i = 0; i < layoutsModel.count; i++) {
var layoutFolder = layoutsModel.get(i, "fileName")
if (layoutFolder === "fallback")
return i
}
return -1
}
function isValidLocale(localeNameOrIndex, ignoreActiveLocales) {
var localeName
if (typeof localeNameOrIndex == "number") {
if (localeNameOrIndex < 0 || localeNameOrIndex >= layoutsModel.count)
return false
localeName = layoutsModel.get(localeNameOrIndex, "fileName")
} else {
localeName = localeNameOrIndex
}
if (!localeName)
return false
if (localeName === "fallback")
return false
if (Qt.locale(localeName).name === "C")
return false
if (ignoreActiveLocales !== true &&
VirtualKeyboardSettings.activeLocales.length > 0 &&
VirtualKeyboardSettings.activeLocales.indexOf(localeName) === -1)
return false
return true
}
function getLayoutFile(localeName, layoutType) {
if (localeName === "" || layoutType === "")
return ""
return layoutsModel.folder + "/" + localeName + "/" + layoutType + ".qml"
}
function getFallbackFile(localeName, layoutType) {
if (localeName === "" || layoutType === "")
return ""
return layoutsModel.folder + "/" + localeName + "/" + layoutType + ".fallback"
}
function layoutExists(localeName, layoutType) {
var result = InputContext.priv.fileExists(getLayoutFile(localeName, layoutType))
if (!result && layoutType === "handwriting")
result = InputContext.priv.fileExists(getFallbackFile(localeName, layoutType))
return result
}
function findLayout(localeName, layoutType) {
var layoutFile = getLayoutFile(localeName, layoutType)
if (InputContext.priv.fileExists(layoutFile))
return layoutFile
var fallbackFile = getFallbackFile(localeName, layoutType)
if (InputContext.priv.fileExists(fallbackFile)) {
layoutFile = getLayoutFile("fallback", layoutType)
if (InputContext.priv.fileExists(layoutFile))
return layoutFile
}
return ""
}
function isHandwritingAvailable() {
if (VirtualKeyboardSettings.handwritingModeDisabled)
return false
return VirtualKeyboardFeatures.Handwriting && layoutExists(locale, "handwriting")
}
function setHandwritingMode(enabled, resetInputMode) {
if (VirtualKeyboardSettings.handwritingModeDisabled)
return
if (enabled && resetInputMode)
inputModeNeedsReset = true
handwritingMode = enabled
}
function notifyLayoutChanged() {
Qt.callLater(function() {
if (keyboardLayoutLoader.item != null) keyboardObserver.layoutChanged()
})
}
function doKeyboardFunction(keyboardFunction) {
if (!isKeyboardFunctionAvailable(keyboardFunction))
return
switch (keyboardFunction) {
case QtVirtualKeyboard.KeyboardFunction.HideInputPanel:
InputContext.priv.hideInputPanel()
break
case QtVirtualKeyboard.KeyboardFunction.ChangeLanguage:
if (style.languagePopupListEnabled) {
if (!languagePopupListActive) {
showLanguagePopup(activeKey, false)
} else {
hideLanguagePopup()
}
} else {
const customLayoutsOnly = arguments.length == 2 && arguments[1]
changeInputLanguage(customLayoutsOnly)
}
break
case QtVirtualKeyboard.KeyboardFunction.ToggleHandwritingMode:
setHandwritingMode(!handwritingMode)
break
default:
console.warn("Unknown keyboard function '%1'".arg(keyboardFunction))
break
}
}
function isKeyboardFunctionAvailable(keyboardFunction) {
switch (keyboardFunction) {
case QtVirtualKeyboard.KeyboardFunction.HideInputPanel:
return true
case QtVirtualKeyboard.KeyboardFunction.ChangeLanguage:
const customLayoutsOnly = arguments.length == 2 && arguments[1]
return canChangeInputLanguage(customLayoutsOnly)
case QtVirtualKeyboard.KeyboardFunction.ToggleHandwritingMode:
return isHandwritingAvailable()
default:
return false
}
}
function isFunctionPopupListAvailable() {
const allFunctionKeys = QtVirtualKeyboard.KeyboardFunctionKeys.Hide |
QtVirtualKeyboard.KeyboardFunctionKeys.Language
return (VirtualKeyboardSettings.visibleFunctionKeys & allFunctionKeys) !== allFunctionKeys ||
isHandwritingAvailable()
}
}