1858 lines
83 KiB
QML
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()
|
|
}
|
|
}
|