HShortcutHandler & HShortcut components

Provide more powerful shortcuts handling than what's available with
QML's Shortcut component, notably being able to react differently to key
presses, releases and auto-repeats.
This commit is contained in:
miruka 2019-08-22 13:03:26 -04:00
parent 3749d1e135
commit 8de26c11a1
9 changed files with 118 additions and 60 deletions

View File

@ -115,7 +115,7 @@ class UISettings(JSONConfigFile):
"theme": "Default.qpl", "theme": "Default.qpl",
"writeAliases": {}, "writeAliases": {},
"keys": { "keys": {
"reloadConfig": ["Alt+Shift+R"], "reloadConfig": "Alt+Shift+R",
"scrollUp": ["Alt+Up", "Alt+K"], "scrollUp": ["Alt+Up", "Alt+K"],
"scrollDown": ["Alt+Down", "Alt+J"], "scrollDown": ["Alt+Down", "Alt+J"],
"focusSidePane": ["Alt+S", "Ctrl+S"], "focusSidePane": ["Alt+S", "Ctrl+S"],

View File

@ -26,5 +26,7 @@ ScrollView {
id: textAreaBackground id: textAreaBackground
color: theme.controls.textArea.background color: theme.controls.textArea.background
} }
Keys.forwardTo: mainUI.shortcuts
} }
} }

View File

@ -0,0 +1,10 @@
import QtQuick 2.12
QtObject {
signal pressed(var event)
signal held(var event)
signal released(var event)
property bool enabled: true
property var sequences: "" // string or array of strings
}

View File

@ -0,0 +1,56 @@
import QtQuick 2.12
Item {
id: shortcutHandler
Keys.onPressed: {
let shortcut = match(event)
if (! shortcut) return
event.isAutoRepeat ? shortcut.held(event) : shortcut.pressed(event)
}
Keys.onReleased: {
let shortcut = match(event)
if (shortcut && ! event.isAutoRepeat) shortcut.released(event)
}
readonly property var modifierDict: ({
Ctrl: Qt.ControlModifier,
Shift: Qt.ShiftModifier,
Alt: Qt.AltModifier,
Meta: Qt.MetaModifier,
})
function sequenceMatches(event, sequence) {
let [key, ...mods] = sequence.split("+").reverse()
key = key.charAt(0).toUpperCase() + key.slice(1)
if (event.key !== Qt["Key_" + key]) return false
for (let [name, code] of Object.entries(modifierDict)) {
if (mods.includes(name) && ! (event.modifiers & code)) return false
if (! mods.includes(name) && event.modifiers & code) return false
}
return true
}
function match(event) {
for (let i = 0; i < shortcutHandler.resources.length; i++) {
let shortcut = shortcutHandler.resources[i]
if (! shortcut.enabled) continue
if (typeof(shortcut.sequences) == "string" &&
sequenceMatches(event, shortcut.sequences)) return shortcut
for (let i = 0; i < shortcut.sequences.length; i++) {
if (sequenceMatches(event, shortcut.sequences[i]))
return shortcut
}
}
return null
}
}

View File

@ -28,4 +28,5 @@ TextField {
} }
selectByMouse: true selectByMouse: true
Keys.forwardTo: mainUI.shortcuts
} }

View File

@ -1,80 +1,54 @@
import QtQuick 2.12 import QtQuick 2.12
import "Base"
import "utils.js" as Utils
Item { HShortcutHandler {
property Item flickTarget: Item {} property Item flickTarget: Item {}
function smartVerticalFlick(baseVelocity, fastMultiply=3) { HShortcut {
if (! flickTarget.interactive) { return } enabled: debugMode
sequences: settings.keys.startDebugger
baseVelocity = -baseVelocity onPressed: py.call("APP.pdb")
let vel = -flickTarget.verticalVelocity
let fast = (baseVelocity < 0 && vel < baseVelocity / 2) ||
(baseVelocity > 0 && vel > baseVelocity / 2)
flickTarget.flick(0, baseVelocity * (fast ? fastMultiply : 1))
} }
HShortcut {
Shortcut { sequences: settings.keys.reloadConfig
sequences: settings.keys ? settings.keys.startDebugger : [] onPressed: py.loadSettings(() => { mainUI.pressAnimation.start() })
onActivated: if (debugMode) { py.call("APP.pdb") }
} }
Shortcut { HShortcut {
sequences: settings.keys ? settings.keys.reloadConfig : [] sequences: settings.keys.scrollUp
onActivated: py.loadSettings(() => { mainUI.pressAnimation.start() }) onPressed: Utils.smartVerticalFlick(flickTarget, -335)
} }
Shortcut { HShortcut {
sequences: settings.keys ? settings.keys.scrollUp : [] sequences: settings.keys.scrollDown
onActivated: smartVerticalFlick(-335) onPressed: Utils.smartVerticalFlick(flickTarget, 335)
} }
Shortcut { HShortcut {
sequences: settings.keys ? settings.keys.scrollDown : [] sequences: settings.keys.focusSidePane
onActivated: smartVerticalFlick(335) onPressed: mainUI.sidePane.setFocus()
} }
Shortcut { HShortcut {
sequences: settings.keys ? settings.keys.focusSidePane : [] sequences: settings.keys.clearRoomFilter
onActivated: mainUI.sidePane.setFocus() onPressed: mainUI.sidePane.paneToolBar.roomFilter = ""
} }
Shortcut { HShortcut {
sequences: settings.keys ? settings.keys.clearRoomFilter : [] sequences: settings.keys.goToPreviousRoom
onActivated: mainUI.sidePane.paneToolBar.roomFilter = "" onPressed: mainUI.sidePane.accountRoomList.previous()
} }
Shortcut { HShortcut {
sequences: settings.keys ? settings.keys.goToPreviousRoom : [] sequences: settings.keys.goToNextRoom
onActivated: mainUI.sidePane.accountRoomList.previous() onPressed: mainUI.sidePane.accountRoomList.next()
} }
Shortcut { HShortcut {
sequences: settings.keys ? settings.keys.goToNextRoom : [] sequences: settings.keys.toggleCollapseAccount
onActivated: mainUI.sidePane.accountRoomList.next() onPressed: mainUI.sidePane.accountRoomList.toggleCollapseAccount()
} }
Shortcut {
sequences: settings.keys ? settings.keys.toggleCollapseAccount : []
onActivated: mainUI.sidePane.accountRoomList.toggleCollapseAccount()
}
/*
Shortcut {
sequence: "Ctrl+-"
onActivated: theme.fontScale = Math.max(0.1, theme.fontScale - 0.1)
}
Shortcut {
sequence: "Ctrl++"
onActivated: theme.fontScale = Math.min(10, theme.fontScale + 0.1)
}
Shortcut {
sequence: "Ctrl+="
onActivated: theme.fontScale = 1.0
}
*/
} }

View File

@ -10,6 +10,7 @@ HRectangle {
color: theme.ui.background color: theme.ui.background
Component.onCompleted: window.mainUI = mainUI Component.onCompleted: window.mainUI = mainUI
property alias shortcuts: shortcuts
property alias sidePane: sidePane property alias sidePane: sidePane
property alias pageLoader: pageLoader property alias pageLoader: pageLoader
property alias pressAnimation: _pressAnimation property alias pressAnimation: _pressAnimation
@ -28,6 +29,9 @@ HRectangle {
(modelSources["Account"] || []).length > 0 || (modelSources["Account"] || []).length > 0 ||
py.startupAnyAccountsSaved py.startupAnyAccountsSaved
Shortcuts { id: shortcuts }
HImage { HImage {
id: mainUIBackground id: mainUIBackground
visible: Boolean(Qt.resolvedUrl(source)) visible: Boolean(Qt.resolvedUrl(source))

View File

@ -27,8 +27,7 @@ ApplicationWindow {
property var theme: null property var theme: null
Shortcuts { id: shortcuts} Python { id: py }
Python { id: py }
HLoader { HLoader {
anchors.fill: parent anchors.fill: parent

View File

@ -162,3 +162,15 @@ function getItem(array, mainKey, value) {
} }
return undefined return undefined
} }
function smartVerticalFlick(flickTarget, baseVelocity, fastMultiply=3) {
if (! flickTarget.interactive) { return }
baseVelocity = -baseVelocity
let vel = -flickTarget.verticalVelocity
let fast = (baseVelocity < 0 && vel < baseVelocity / 2) ||
(baseVelocity > 0 && vel > baseVelocity / 2)
flickTarget.flick(0, baseVelocity * (fast ? fastMultiply : 1))
}