Implement customizable actions

This commit is contained in:
Renaud Casenave-Péré 2022-12-30 10:34:49 +01:00
parent fd81e24976
commit f5510a7944
11 changed files with 282 additions and 38 deletions

View file

@ -0,0 +1,62 @@
(uiop:define-package :sextant/models/actions
(:use :cl :eql :qml :options
:sextant/models/org-model))
(in-package :sextant/models/actions)
(defmacro defaction (action-name types &body body)
(declare (ignore types))
`(prog1
(defun ,action-name (ui-item node coords)
(declare (ignorable ui-item node coords))
,@body
nil)
(push-action (symbol-name ',action-name) ',types)))
(defaction raw-edit (org-line org-headline)
(qjs |setFocus| ui-item)
(qjs |setCursorPositionAt| ui-item (car coords) (cdr coords))
(qjs |editRawText| ui-item))
(defaction nothing (org-line org-headline)
)
(defmacro defaction-type (type)
`(progn
,(let ((symbol (intern (concatenate 'string (symbol-name type) "-CLICKED")))
(funsym (intern (concatenate 'string "CLICK-ON-" (symbol-name type))
:sextant/options/options)))
`(prog1
(defun ,symbol (index x y)
(let ((funsym ,funsym))
(when funsym
(let ((fun (intern funsym #.*package*)))
(when (fboundp fun)
(funcall fun *caller* (goto-index index) (cons x y)))))))
(export ',symbol)))
,(let ((symbol (intern (concatenate 'string (symbol-name type) "-DOUBLE-CLICKED")))
(funsym (intern (concatenate 'string "DOUBLE-CLICK-ON-" (symbol-name type))
:sextant/options/options)))
`(prog1
(defun ,symbol (index x y)
(let ((funsym ,funsym))
(when funsym
(let ((fun (intern funsym #.*package*)))
(when (fboundp fun)
(funcall fun *caller* (goto-index index) (cons x y)))))))
(export ',symbol)))
,(let ((symbol (intern (concatenate 'string (symbol-name type) "-PRESS-AND-HOLD")))
(funsym (intern (concatenate 'string "PRESS-AND-HOLD-ON-" (symbol-name type))
:sextant/options/options)))
`(prog1
(defun ,symbol (index x y)
(let ((funsym ,funsym))
(when funsym
(let ((fun (intern funsym #.*package*)))
(when (fboundp fun)
(funcall fun *caller* (goto-index index) (cons x y)))))))
(export ',symbol)))))
(defaction-type org-line)
(defaction-type org-headline)

View file

@ -4,4 +4,5 @@
:sextant/models/utils
:sextant/models/files-model
:sextant/models/commands
:sextant/models/actions
:sextant/models/org-model))

View file

@ -1,5 +1,6 @@
(uiop:define-package :sextant/options/options
(:use :cl :sextant/options/config))
(:use :cl :sextant/options/config)
(:export #:push-action))
(in-package :sextant/options/options)
(set-config-package :sextant/options/options)
@ -20,4 +21,33 @@
(defconfig undo-history-size 100)
(defconfig recent-files-size 10)
(eval-when (:compile-toplevel :load-toplevel)
(defun push-action (action-name types)
(dolist (type types)
(pushnew action-name (get (intern (concatenate 'string (symbol-name type) "-ACTIONS")
#.*package*)
'actions)))))
(defmacro defaction-config (choices-list-symbol &rest config-name/value)
`(progn
,(let ((list-getter (intern (concatenate 'string "GET-" (symbol-name choices-list-symbol)))))
`(prog1
(defun ,list-getter (index)
(nth index (get ',choices-list-symbol 'actions)))
(export ',list-getter)))
,@(loop for action on config-name/value by #'cddr
collect `(defconfig ,(first action) ,(second action)
:set ((index)
(setf ,(first action) (nth index (get ',choices-list-symbol 'actions))))))))
(defaction-config org-line-actions
click-on-org-line "RAW-EDIT"
double-click-on-org-line "NOTHING"
press-and-hold-on-org-line "NOTHING")
(defaction-config org-headline-actions
click-on-org-headline "RAW-EDIT"
double-click-on-org-headline "NOTHING"
press-and-hold-on-org-headline "NOTHING")
(defconfig slynk-at-startup-p nil)

View file

@ -0,0 +1,43 @@
import QtQuick 2.0
import Sailfish.Silica 1.0
import EQL5 1.0
ComboBox {
id: actionComboBox
width: parent.width
property string list
property string getter
property string setter
menu: ContextMenu {
Repeater {
model: ListModel {
function populate () {
var current = Lisp.call(getter)
var i = 0
var item = Lisp.call(list, i)
while (item) {
append({inputText: item})
if (item == current)
actionComboBox.currentIndex = i
item = Lisp.call(list, ++i)
}
}
Component.onCompleted: populate()
}
MenuItem {
text: inputText
}
}
}
onCurrentItemChanged: {
Lisp.call(setter, currentIndex)
}
}

View file

@ -38,4 +38,5 @@ Item {
function forceCommit (update) { loader.item.forceCommit(update) }
function setCursorPositionAtEnd (fix) { loader.item.setCursorPositionAtEnd(fix) }
function editRawText () { loader.item.editRawText() }
}

View file

@ -10,6 +10,8 @@ TextArea {
property bool textModified: false
property int fixedCursorPosition: -1
signal loseFocus
text: index > 0 ? sentinelChar + rawtext : (index == 0 ? rawtext : "")
anchors {
@ -24,10 +26,7 @@ TextArea {
labelVisible: false
inputMethodHints: Qt.ImhNoAutoUppercase
onCursorPositionChanged: {
if (index > 0 && cursorPosition == 0)
cursorPosition = 1
}
onCursorPositionChanged: { refreshCursorPosition() }
onSelectionStartChanged: {
if (index > 0 && selectionStart == 0)
@ -36,23 +35,23 @@ TextArea {
onTextChanged: {
if (visible) {
if (fixedCursorPosition != -1) {
cursorPosition = fixedCursorPosition
fixedCursorPosition = -1
}
refreshCursorPosition()
if (index >= 0) {
if (index > 0 && text[0] != sentinelChar) {
var textEmpty = text.length != 0
var textEmpty = text.length == 0
forceCommit(false)
document.focusedIndex = index - 1
document.focusedItem.setCursorPositionAtEnd(textEmpty)
var focusedItem = document.focusedItem
focusedItem.setCursorPositionAtEnd(!textEmpty)
Lisp.call("models:join-node", index - 1, true)
focusedItem.editRawText()
} else {
var split = text.indexOf("\n")
if (split != -1) {
forceCommit(false)
Lisp.call("models:split-node", index, text.substring(index > 0 ? 1 : 0, split), text.substring(split + 1), true)
document.focusedIndex = index + 1
document.focusedItem.editRawText()
} else {
lastText = getText()
textModified = lastText != rawtext
@ -71,6 +70,7 @@ TextArea {
document.focusedIndex = -1
forceCommit(true)
loseFocus()
}
}
@ -84,11 +84,20 @@ TextArea {
function initFocus() {
if (visible) {
forceActiveFocus()
if (index > 0 && cursorPosition == 0)
cursorPosition = 1
refreshCursorPosition()
}
}
function refreshCursorPosition () {
if (fixedCursorPosition != -1) {
cursorPosition = fixedCursorPosition
fixedCursorPosition = -1
}
if (index > 0 && cursorPosition == 0)
cursorPosition = 1
}
function forceCommit (update) {
if (textModified) {
textModified = false
@ -102,6 +111,7 @@ TextArea {
function setCursorPositionAtEnd (fix) {
if (fix) {
console.log("fixed " + text.length)
fixedCursorPosition = text.length
} else {
cursorPosition = text.length

View file

@ -1,11 +1,11 @@
import QtQuick 2.0
import Sailfish.Silica 1.0
import EQL5 1.0
OrgText {
id: orgHeadline
contentHeight: titleLabel.contentHeight
editing: orgItem.focused
Label {
id: bullet
@ -20,7 +20,7 @@ OrgText {
family: Theme.fontFamilyHeading
}
visible: !orgItem.focused
visible: !editing
text: "*"
}
@ -39,8 +39,20 @@ OrgText {
family: Theme.fontFamilyHeading
}
visible: !orgItem.focused
visible: !editing
text: title
wrapMode: Text.Wrap
}
onClicked: {
Lisp.call(this, "models:org-headline-clicked", index, mouse.x, mouse.y)
}
onDoubleClicked: {
Lisp.call(this, "models:org-headline-double-clicked", index, mouse.x, mouse.y)
}
onPressAndHold: {
Lisp.call(this, "models:org-headline-press-and-hold", index, mouse.x, mouse.y)
}
}

View file

@ -1,11 +1,11 @@
import QtQuick 2.0
import Sailfish.Silica 1.0
import EQL5 1.0
OrgText {
id: orgLine
contentHeight: label.contentHeight
editing: orgItem.focused
Label {
id: label
@ -16,8 +16,21 @@ OrgText {
rightMargin: Theme.paddingSmall
}
visible: !orgItem.focused
visible: !editing
text: rawtext
wrapMode: Text.Wrap
}
onClicked: {
Lisp.call(this, "models:org-line-clicked", index, mouse.x, mouse.y)
}
onDoubleClicked: {
Lisp.call(this, "models:org-line-double-clicked", index, mouse.x, mouse.y)
}
onPressAndHold: {
Lisp.call(this, "models:org-line-press-and-hold", index, mouse.x, mouse.y)
}
}

View file

@ -17,12 +17,14 @@ MouseArea {
OrgEdit {
id: edit
visible: editing
onLoseFocus: {
editing = false
}
}
onClicked: {
edit.setCursorPositionAt(mouse.x, mouse.y)
document.focusedIndex = index
}
function setFocus() { document.focusedIndex = index }
function editRawText() { editing = true }
function forceCommit (update) { edit.forceCommit(update) }
function setCursorPositionAt (x, y) { edit.setCursorPositionAt(x, y) }

View file

@ -48,23 +48,91 @@ Page {
onValueChanged: Lisp.call("options:set-recent-files-size", value)
}
SectionHeader {
text: qsTr("Development")
}
ExpandingSectionGroup {
currentIndex: -1
TextSwitch {
text: qsTr("Enable Slynk at startup")
checked: Lisp.call("options:get-slynk-at-startup-p")
onCheckedChanged: Lisp.call("options:set-slynk-at-startup-p", checked)
}
ExpandingSection {
id: actions
title: qsTr("Actions")
Button {
text: qsTr("Start")
visible: false
enabled: !Lisp.call("sextant:slynkp")
onClicked: {
Lisp.call("sextant:start-slynk")
enabled = !Lisp.call("sextant:slynkp")
content.sourceComponent: Column {
width: actions.width
SectionHeader {
text: qsTr("Line")
}
ActionComboBox {
label: qsTr("On clicked")
list: "options:get-org-line-actions"
getter: "options:get-click-on-org-line"
setter: "options:set-click-on-org-line"
}
ActionComboBox {
label: qsTr("On double-clicked")
list: "options:get-org-line-actions"
getter: "options:get-double-click-on-org-line"
setter: "options:set-double-click-on-org-line"
}
ActionComboBox {
label: qsTr("On press and hold")
list: "options:get-org-line-actions"
getter: "options:get-press-and-hold-on-org-line"
setter: "options:set-press-and-hold-on-org-line"
}
SectionHeader {
text: qsTr("Headline")
}
ActionComboBox {
label: qsTr("On clicked")
list: "options:get-org-headline-actions"
getter: "options:get-click-on-org-headline"
setter: "options:set-click-on-org-headline"
}
ActionComboBox {
label: qsTr("On double-clicked")
list: "options:get-org-headline-actions"
getter: "options:get-double-click-on-org-headline"
setter: "options:set-double-click-on-org-headline"
}
ActionComboBox {
label: qsTr("On press and hold")
list: "options:get-org-headline-actions"
getter: "options:get-press-and-hold-on-org-headline"
setter: "options:set-press-and-hold-on-org-headline"
}
}
}
ExpandingSection {
id: development
title: qsTr("Development")
content.sourceComponent: Column {
width: section.width
TextSwitch {
text: qsTr("Enable Slynk at startup")
checked: Lisp.call("options:get-slynk-at-startup-p")
onCheckedChanged: Lisp.call("options:set-slynk-at-startup-p", checked)
}
Button {
text: qsTr("Start")
visible: false
enabled: !Lisp.call("sextant:slynkp")
onClicked: {
Lisp.call("sextant:start-slynk")
enabled = !Lisp.call("sextant:slynkp")
}
}
}
}
}
}

View file

@ -20,8 +20,9 @@ LISP_FILES = make.lisp \
lisp/local-projects/sextant/models/utils.lisp \
lisp/local-projects/sextant/models/org-model.lisp \
lisp/local-projects/sextant/models/files-model.lisp \
lisp/local-projects/sextant/models/all.lisp \
lisp/local-projects/sextant/models/commands.lisp \
lisp/local-projects/sextant/models/actions.lisp \
lisp/local-projects/sextant/models/all.lisp \
lisp/local-projects/sextant/sextant.lisp \
lisp/local-projects/sextant/sextant.asd \
lisp/local-projects/sextant/org/parser.lisp \
@ -57,6 +58,7 @@ SOURCES += src/harbour-sextant.cc
DISTFILES += qml/harbour-sextant.qml \
qml/cover/CoverPage.qml \
qml/components/ListTextField.qml \
qml/components/ActionComboBox.qml \
qml/components/OrgDelegate.qml \
qml/components/OrgLine.qml \
qml/components/OrgText.qml \