mirror of
https://gitlab.com/eql/lqml.git
synced 2025-12-06 02:30:38 -08:00
add Qt6 version of 'cl-repl'
This commit is contained in:
parent
e8866954c0
commit
07f65d6a20
29 changed files with 1553 additions and 0 deletions
2
examples/Qt6/cl-repl/.gitignore
vendored
Normal file
2
examples/Qt6/cl-repl/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
*
|
||||||
|
!.gitignore
|
||||||
64
examples/Qt6/cl-repl/platforms/android/AndroidManifest.xml
Normal file
64
examples/Qt6/cl-repl/platforms/android/AndroidManifest.xml
Normal file
|
|
@ -0,0 +1,64 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
package="org.eql5.android.repl"
|
||||||
|
android:versionName="1.0.67"
|
||||||
|
android:versionCode="130"
|
||||||
|
android:installLocation="auto">
|
||||||
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||||
|
|
||||||
|
<supports-screens
|
||||||
|
android:anyDensity="true"
|
||||||
|
android:largeScreens="true"
|
||||||
|
android:normalScreens="true"
|
||||||
|
android:smallScreens="true" />
|
||||||
|
<application
|
||||||
|
android:name="org.qtproject.qt.android.bindings.QtApplication"
|
||||||
|
android:label="cl-repl"
|
||||||
|
android:icon="@drawable/icon"
|
||||||
|
android:hardwareAccelerated="true"
|
||||||
|
android:requestLegacyExternalStorage="true"
|
||||||
|
android:allowBackup="true"
|
||||||
|
android:fullBackupOnly="false">
|
||||||
|
<activity
|
||||||
|
android:name="org.qtproject.qt.android.bindings.QtActivity"
|
||||||
|
android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|layoutDirection|locale|fontScale|keyboard|keyboardHidden|navigation|mcc|mnc|density"
|
||||||
|
android:label="cl-repl"
|
||||||
|
android:theme="@style/splashScreenTheme"
|
||||||
|
android:launchMode="singleTop"
|
||||||
|
android:screenOrientation="unspecified"
|
||||||
|
android:exported="true">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
|
</intent-filter>
|
||||||
|
|
||||||
|
<meta-data
|
||||||
|
android:name="android.app.lib_name"
|
||||||
|
android:value="app" />
|
||||||
|
|
||||||
|
<meta-data
|
||||||
|
android:name="android.app.arguments"
|
||||||
|
android:value="" />
|
||||||
|
|
||||||
|
<meta-data
|
||||||
|
android:name="android.app.extract_android_style"
|
||||||
|
android:value="minimal" />
|
||||||
|
|
||||||
|
<meta-data
|
||||||
|
android:name="android.app.splash_screen_drawable"
|
||||||
|
android:resource="@drawable/splashscreen"/>
|
||||||
|
</activity>
|
||||||
|
|
||||||
|
<provider
|
||||||
|
android:name="androidx.core.content.FileProvider"
|
||||||
|
android:authorities="${applicationId}.qtprovider"
|
||||||
|
android:exported="false"
|
||||||
|
android:grantUriPermissions="true">
|
||||||
|
<meta-data
|
||||||
|
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||||
|
android:resource="@xml/qtprovider_paths"/>
|
||||||
|
</provider>
|
||||||
|
</application>
|
||||||
|
</manifest>
|
||||||
17
examples/Qt6/cl-repl/qml/ext/ArrowButton.qml
Normal file
17
examples/Qt6/cl-repl/qml/ext/ArrowButton.qml
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Controls.Basic
|
||||||
|
|
||||||
|
Button {
|
||||||
|
width: main.small ? 33 : 45
|
||||||
|
height: width
|
||||||
|
flat: true
|
||||||
|
focusPolicy: Qt.NoFocus
|
||||||
|
font.family: fontAwesome.name
|
||||||
|
font.pixelSize: 1.2 * width
|
||||||
|
opacity: 0.12
|
||||||
|
scale: 1.2
|
||||||
|
|
||||||
|
onPressed: Lisp.call(this, "editor:button-pressed")
|
||||||
|
onPressAndHold: Lisp.call(this, "editor:button-pressed-and-helt")
|
||||||
|
}
|
||||||
13
examples/Qt6/cl-repl/qml/ext/Button.qml
Normal file
13
examples/Qt6/cl-repl/qml/ext/Button.qml
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Controls.Basic
|
||||||
|
|
||||||
|
Button {
|
||||||
|
width: main.small ? 40 : 60
|
||||||
|
height: main.small ? 37 : 55
|
||||||
|
font.family: fontAwesome.name
|
||||||
|
font.pixelSize: main.small ? 25 : 36
|
||||||
|
focusPolicy: Qt.NoFocus
|
||||||
|
|
||||||
|
onPressed: Lisp.call(this, "editor:button-pressed")
|
||||||
|
}
|
||||||
36
examples/Qt6/cl-repl/qml/ext/ClipboardMenu.qml
Normal file
36
examples/Qt6/cl-repl/qml/ext/ClipboardMenu.qml
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Controls.Basic
|
||||||
|
import "." as Ext
|
||||||
|
|
||||||
|
Popup {
|
||||||
|
objectName: "clipboard_menu"
|
||||||
|
x: (main.width - width) / 2
|
||||||
|
y: 4
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: menuButtonRow
|
||||||
|
spacing: 6
|
||||||
|
|
||||||
|
Ext.MenuButton {
|
||||||
|
objectName: "select_all"
|
||||||
|
text: "\uf07d"
|
||||||
|
}
|
||||||
|
Ext.MenuButton {
|
||||||
|
objectName: "cut"
|
||||||
|
text: "\uf0c4"
|
||||||
|
}
|
||||||
|
Ext.MenuButton {
|
||||||
|
objectName: "copy"
|
||||||
|
text: "\uf0c5"
|
||||||
|
}
|
||||||
|
Ext.MenuButton {
|
||||||
|
objectName: "paste"
|
||||||
|
text: "\uf0ea"
|
||||||
|
}
|
||||||
|
Ext.MenuButton {
|
||||||
|
objectName: "eval_exp"
|
||||||
|
text: "\u03bb" // lambda
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
85
examples/Qt6/cl-repl/qml/ext/DebugDialog.qml
Normal file
85
examples/Qt6/cl-repl/qml/ext/DebugDialog.qml
Normal file
|
|
@ -0,0 +1,85 @@
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Controls.Basic
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import "." as Ext
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: debugDialog
|
||||||
|
objectName: "debug_dialog"
|
||||||
|
color: "#f0f0f0"
|
||||||
|
visible: false
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
anchors.fill: parent
|
||||||
|
spacing: 0
|
||||||
|
|
||||||
|
Ext.MenuBack {
|
||||||
|
id: menuBack
|
||||||
|
Layout.fillWidth: true
|
||||||
|
label: "Debug Dialog"
|
||||||
|
}
|
||||||
|
|
||||||
|
TextField {
|
||||||
|
id: debugInput
|
||||||
|
objectName: "debug_input"
|
||||||
|
Layout.fillWidth: true
|
||||||
|
font.family: "Hack"
|
||||||
|
font.pixelSize: 18
|
||||||
|
inputMethodHints: Qt.ImhNoAutoUppercase | Qt.ImhNoPredictiveText
|
||||||
|
text: ":q"
|
||||||
|
|
||||||
|
onAccepted: Lisp.call("dialogs:exited")
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: label
|
||||||
|
Layout.fillWidth: true
|
||||||
|
leftPadding: 8
|
||||||
|
rightPadding: 8
|
||||||
|
topPadding: 8
|
||||||
|
bottomPadding: 8
|
||||||
|
font.family: "Hack"
|
||||||
|
font.pixelSize: 14
|
||||||
|
text: ":r1 etc. restart / :h help / :q quit"
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: line
|
||||||
|
Layout.fillWidth: true
|
||||||
|
height: 1
|
||||||
|
color: "#d0d0d0"
|
||||||
|
}
|
||||||
|
|
||||||
|
ListView {
|
||||||
|
id: debugText
|
||||||
|
objectName: "debug_text"
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
contentWidth: parent.width * 5
|
||||||
|
clip: true
|
||||||
|
model: debugModel
|
||||||
|
flickableDirection: Flickable.HorizontalAndVerticalFlick
|
||||||
|
|
||||||
|
delegate: Text {
|
||||||
|
padding: 8
|
||||||
|
textFormat: Text.PlainText
|
||||||
|
font.pixelSize: 16
|
||||||
|
font.family: "Hack"
|
||||||
|
font.bold: model.bold
|
||||||
|
text: model.text
|
||||||
|
color: model.color
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ListModel {
|
||||||
|
id: debugModel
|
||||||
|
objectName: "debug_model"
|
||||||
|
|
||||||
|
function appendOutput(data) {
|
||||||
|
append(data)
|
||||||
|
debugText.positionViewAtEnd()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
23
examples/Qt6/cl-repl/qml/ext/Dynamic.qml
Normal file
23
examples/Qt6/cl-repl/qml/ext/Dynamic.qml
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
import QtQuick
|
||||||
|
|
||||||
|
Item {
|
||||||
|
objectName: "dynamic"
|
||||||
|
|
||||||
|
property Component component
|
||||||
|
property Item item
|
||||||
|
|
||||||
|
function createItem(file) {
|
||||||
|
// for custom QML items to be loaded on top of REPL app
|
||||||
|
if (item != null) {
|
||||||
|
item.destroy()
|
||||||
|
}
|
||||||
|
Engine.clearCache()
|
||||||
|
var pre = (Qt.platform.os === "windows") ? "file:/" : "file://"
|
||||||
|
component = Qt.createComponent(pre + file)
|
||||||
|
if (component.status === Component.Ready) {
|
||||||
|
item = component.createObject()
|
||||||
|
return item
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
165
examples/Qt6/cl-repl/qml/ext/FileBrowser.qml
Normal file
165
examples/Qt6/cl-repl/qml/ext/FileBrowser.qml
Normal file
|
|
@ -0,0 +1,165 @@
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Controls.Basic
|
||||||
|
import Qt.labs.folderlistmodel
|
||||||
|
import "." as Ext
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: fileBrowser
|
||||||
|
objectName: "file_browser"
|
||||||
|
visible: false
|
||||||
|
|
||||||
|
property bool editMode: false
|
||||||
|
property string editFrom
|
||||||
|
|
||||||
|
function urlToString(url) {
|
||||||
|
var cut = (Qt.platform.os === "windows") ? "file:///" : "file://"
|
||||||
|
return url.toString().substring(cut.length)
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: header
|
||||||
|
width: fileBrowser.width
|
||||||
|
height: headerColumn.height
|
||||||
|
z: 2
|
||||||
|
color: "#f0f0f0"
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: headerColumn
|
||||||
|
|
||||||
|
Ext.MenuBack {
|
||||||
|
id: menuBack
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: buttonRow
|
||||||
|
spacing: 4
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
|
||||||
|
// one directory up
|
||||||
|
Ext.FileButton {
|
||||||
|
text: "\uf062"
|
||||||
|
onClicked: Lisp.call("dialogs:set-file-browser-path",
|
||||||
|
urlToString(folderModel.parentFolder))
|
||||||
|
}
|
||||||
|
|
||||||
|
// documents
|
||||||
|
Ext.FileButton {
|
||||||
|
text: "\uf0f6"
|
||||||
|
onClicked: Lisp.call("dialogs:set-file-browser-path", ":data")
|
||||||
|
}
|
||||||
|
|
||||||
|
// home
|
||||||
|
Ext.FileButton {
|
||||||
|
text: "\uf015"
|
||||||
|
onClicked: Lisp.call("dialogs:set-file-browser-path", ":home")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ext.TextField {
|
||||||
|
id: path
|
||||||
|
objectName: "path"
|
||||||
|
width: fileBrowser.width
|
||||||
|
inputMethodHints: Qt.ImhNoAutoUppercase | Qt.ImhNoPredictiveText
|
||||||
|
|
||||||
|
onFocusChanged: if (focus) { cursorPosition = length }
|
||||||
|
|
||||||
|
onAccepted: {
|
||||||
|
if (fileBrowser.editMode) {
|
||||||
|
Lisp.call("dialogs:rename-file*", fileBrowser.editFrom, path.text)
|
||||||
|
fileBrowser.editMode = false
|
||||||
|
} else {
|
||||||
|
Lisp.call("dialogs:set-file-name", text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// edit mode
|
||||||
|
Ext.FileButton {
|
||||||
|
id: fileEdit
|
||||||
|
objectName: "file_edit"
|
||||||
|
anchors.right: parent.right
|
||||||
|
contentItem: Text {
|
||||||
|
id: editButton
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
text: "\uf044"
|
||||||
|
color: fileBrowser.editMode ? "red" : "#007aff"
|
||||||
|
font.family: fontAwesome.name
|
||||||
|
font.pixelSize: 24
|
||||||
|
}
|
||||||
|
|
||||||
|
onClicked: fileBrowser.editMode = !fileBrowser.editMode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ListView {
|
||||||
|
id: folderView
|
||||||
|
objectName: "folder_view"
|
||||||
|
y: header.height
|
||||||
|
width: parent.width
|
||||||
|
height: parent.height - y
|
||||||
|
delegate: Ext.FileDelegate {}
|
||||||
|
currentIndex: -1 // no initial highlight
|
||||||
|
footerPositioning: ListView.OverlayHeader
|
||||||
|
|
||||||
|
property var colors: ["white", "#f0f0f0"]
|
||||||
|
|
||||||
|
model: FolderListModel {
|
||||||
|
id: folderModel
|
||||||
|
objectName: "folder_model"
|
||||||
|
showDirsFirst: true
|
||||||
|
showHidden: true
|
||||||
|
nameFilters: ["*.lisp", "*.lsp", "*.qml", "*.asd", "*.exp", "*.sexp",
|
||||||
|
"*.fas", "*.fasb", "*.fasc", ".eclrc", ".repl-history"]
|
||||||
|
|
||||||
|
onFolderChanged: path.text = urlToString(folder)
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
y: main.small ? 7 : 10
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
spacing: 20
|
||||||
|
visible: Lisp.call("qml:mobile-p") ? path.focus : false
|
||||||
|
|
||||||
|
// cursor back
|
||||||
|
Ext.ArrowButton {
|
||||||
|
opacity: 0.15
|
||||||
|
text: "\uf137"
|
||||||
|
|
||||||
|
onPressed: path.cursorPosition--
|
||||||
|
onPressAndHold: path.cursorPosition = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// cursor forward
|
||||||
|
Ext.ArrowButton {
|
||||||
|
opacity: 0.15
|
||||||
|
text: "\uf138"
|
||||||
|
|
||||||
|
onPressed: path.cursorPosition++
|
||||||
|
onPressAndHold: path.cursorPosition = path.length
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
footer: Rectangle {
|
||||||
|
width: fileBrowser.width
|
||||||
|
height: itemCount.height + 4
|
||||||
|
z: 2
|
||||||
|
color: "lightgray"
|
||||||
|
border.width: 1
|
||||||
|
border.color: "gray"
|
||||||
|
|
||||||
|
Row {
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: itemCount
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
text: Lisp.call("cl:format", null, " ~D item~P", folderModel.count, folderModel.count)
|
||||||
|
font.pixelSize: 18
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
11
examples/Qt6/cl-repl/qml/ext/FileButton.qml
Normal file
11
examples/Qt6/cl-repl/qml/ext/FileButton.qml
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Controls.Basic
|
||||||
|
|
||||||
|
Button {
|
||||||
|
width: main.small ? 42 : 48
|
||||||
|
height: width
|
||||||
|
font.family: fontAwesome.name
|
||||||
|
font.pixelSize: 24
|
||||||
|
flat: true
|
||||||
|
}
|
||||||
64
examples/Qt6/cl-repl/qml/ext/FileDelegate.qml
Normal file
64
examples/Qt6/cl-repl/qml/ext/FileDelegate.qml
Normal file
|
|
@ -0,0 +1,64 @@
|
||||||
|
import QtQuick
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: folderView.width
|
||||||
|
height: 48
|
||||||
|
color: (index === folderView.currentIndex) ? "lightskyblue" : folderView.colors[index & 1]
|
||||||
|
|
||||||
|
Row {
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: icon
|
||||||
|
width: 38
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
font.family: fontAwesome.name
|
||||||
|
font.pixelSize: 24
|
||||||
|
text: fileIsDir ? " \uf115" : " \uf016"
|
||||||
|
}
|
||||||
|
Text {
|
||||||
|
width: 3/4 * folderView.width - icon.width
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
font.pixelSize: 18
|
||||||
|
text: fileName
|
||||||
|
}
|
||||||
|
Text {
|
||||||
|
width: 1/4 * folderView.width - 4
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
horizontalAlignment: Text.AlignRight
|
||||||
|
font.pixelSize: 18
|
||||||
|
text: fileIsDir ? "" : Lisp.call("cl:format", null, "~:D", fileSize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
// highlight selected
|
||||||
|
folderView.currentIndex = index
|
||||||
|
Lisp.call("qml:qsleep", 0.1)
|
||||||
|
folderView.currentIndex = -1
|
||||||
|
|
||||||
|
if (fileBrowser.editMode) {
|
||||||
|
path.text = filePath
|
||||||
|
fileBrowser.editFrom = filePath
|
||||||
|
path.forceActiveFocus()
|
||||||
|
var start = filePath.lastIndexOf("/") + 1
|
||||||
|
var end = filePath.lastIndexOf(".")
|
||||||
|
if (end > start) {
|
||||||
|
path.cursorPosition = start
|
||||||
|
path.moveCursorSelection(end, TextInput.SelectCharacters)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (fileIsDir) {
|
||||||
|
Lisp.call("dialogs:set-file-browser-path", filePath)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
fileBrowser.visible = false
|
||||||
|
Lisp.call("dialogs:set-file-name", filePath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
23
examples/Qt6/cl-repl/qml/ext/Flickable.qml
Normal file
23
examples/Qt6/cl-repl/qml/ext/Flickable.qml
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Controls.Basic
|
||||||
|
import "." as Ext
|
||||||
|
|
||||||
|
Flickable {
|
||||||
|
clip: true
|
||||||
|
|
||||||
|
ScrollBar.vertical: Ext.ScrollBar {}
|
||||||
|
|
||||||
|
function ensureVisible(r) {
|
||||||
|
if (main.skipEnsureVisible)
|
||||||
|
return;
|
||||||
|
if (contentX >= r.x)
|
||||||
|
contentX = r.x;
|
||||||
|
else if (contentX + width <= r.x + r.width)
|
||||||
|
contentX = r.x + r.width - width;
|
||||||
|
if (contentY >= r.y)
|
||||||
|
contentY = r.y;
|
||||||
|
else if (contentY + height <= r.y + r.height)
|
||||||
|
contentY = r.y + r.height - height;
|
||||||
|
}
|
||||||
|
}
|
||||||
119
examples/Qt6/cl-repl/qml/ext/Help.qml
Normal file
119
examples/Qt6/cl-repl/qml/ext/Help.qml
Normal file
|
|
@ -0,0 +1,119 @@
|
||||||
|
import QtQuick
|
||||||
|
import "." as Ext
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
color: "lightyellow"
|
||||||
|
visible: false
|
||||||
|
|
||||||
|
Column {
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
Ext.MenuBack {
|
||||||
|
label: "Help"
|
||||||
|
}
|
||||||
|
|
||||||
|
Ext.Flickable {
|
||||||
|
id: flick
|
||||||
|
width: parent.width
|
||||||
|
height: parent.height
|
||||||
|
contentWidth: help.paintedWidth
|
||||||
|
contentHeight: help.paintedHeight + 100
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: help
|
||||||
|
width: flick.width
|
||||||
|
padding: 10
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
font.pixelSize: 18
|
||||||
|
textFormat: Text.RichText
|
||||||
|
text:
|
||||||
|
"
|
||||||
|
<h3>Eval line commands</h3>
|
||||||
|
<table cellpadding=5>
|
||||||
|
<tr>
|
||||||
|
<td align=right><b>:?</b></td><td>find regular expression, e.g.<br><font face=\"Hack\">:? prin[c1]</font><br>hit RET for next match</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td align=right><b>*</b></td><td>copy eval value to clipboard</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td align=right><b>:c</b></td><td>clear all output</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td align=right><b>:k</b></td><td>kill eval thread (long running task)</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td align=right><b>:s</b></td><td>start Swank server</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td align=right><b>:q</b></td><td>load Quicklisp</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td align=right><b>:w</b></td><td>start local web-server for file upload/download, see<br><font face=\"Hack\">http://192.168.1.x:1701/</font>
|
||||||
|
<br>(not encrypted)</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td align=right><b>:ws</b></td><td>stop local web-server</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<br>
|
||||||
|
<h3>Special keys/taps</h3>
|
||||||
|
<table cellpadding=5>
|
||||||
|
<tr>
|
||||||
|
<td align=right><b>double SPC</b></td><td>auto completion, e.g.<b> m-v-b</b></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td align=right><b>tap and hold</b></td><td>in editor to select/copy/paste/eval s-expression, e.g. on <b>defun</b></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td align=right><b>tap and hold</b></td><td>cursor buttons to move to beginning/end of line/file</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td align=right><b>hold ')'</b></td><td>(paren buttons) to close all open parens</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<br>
|
||||||
|
<h3>Special functions</h3>
|
||||||
|
<table cellpadding=5>
|
||||||
|
<tr>
|
||||||
|
<td align=right><b>print</b></td>
|
||||||
|
<td>
|
||||||
|
<font face=\"Hack\">(ed:pr \"greetings\" :color \"red\" :bold t :line t)</font>
|
||||||
|
<br>pass <font face=\"Hack\"> :rich-text t </font> if you use (a subset of) <b>html</b>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
%2
|
||||||
|
%3
|
||||||
|
</table>
|
||||||
|
<br>
|
||||||
|
<h3>External keyboard</h3>
|
||||||
|
<table cellpadding=5>
|
||||||
|
<tr>
|
||||||
|
<td align=right><b>[Up]</b></td><td>move back in eval line history</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td align=right><b>[Down]</b></td><td>move forward in eval line history</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td align=right><b>[Tab]</b></td><td>switch focus between editor / eval line</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td align=right><b>[%1+E]</b></td><td><b>E</b>xpression: select s-exp</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td align=right><b>[%1+L]</b></td><td><b>L</b>ambda: eval selected s-exp</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
".arg((Qt.platform.os === "ios")
|
||||||
|
? "Alt" : ((Qt.platform.os === "osx")
|
||||||
|
? "Cmd" : "Ctrl"))
|
||||||
|
.arg((Qt.platform.os === "android")
|
||||||
|
? "<tr><td align=right><b>shell</b></td><td><font face=\"Hack\">(shell \"df -h\")</font></td></tr>"
|
||||||
|
: "")
|
||||||
|
.arg(((Qt.platform.os === "android") || (Qt.platform.os === "ios"))
|
||||||
|
? "<tr><td align=right><b>zip</b></td><td><font face=\"Hack\">(zip \"all.zip\" \"doc\")</font></td></tr><tr><td align=right><b>unzip</b></td><td><font face=\"Hack\">(unzip \"uploads/all.zip\" \"doc\")</font></td></tr>"
|
||||||
|
: "")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
54
examples/Qt6/cl-repl/qml/ext/MenuBack.qml
Normal file
54
examples/Qt6/cl-repl/qml/ext/MenuBack.qml
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Controls.Basic
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: menuBack
|
||||||
|
width: main.width
|
||||||
|
height: backButton.height
|
||||||
|
color: "#f0f0f0"
|
||||||
|
|
||||||
|
property alias label: label.text
|
||||||
|
|
||||||
|
Button {
|
||||||
|
id: backButton
|
||||||
|
height: main.small ? 40 : 46
|
||||||
|
width: 80
|
||||||
|
|
||||||
|
background: Rectangle {
|
||||||
|
Text {
|
||||||
|
id: iconBack
|
||||||
|
x: 10
|
||||||
|
height: backButton.height
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
font.family: fontAwesome.name
|
||||||
|
font.pixelSize: 32
|
||||||
|
color: "#007aff"
|
||||||
|
text: "\uf104"
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
x: 30
|
||||||
|
height: backButton.height * 1.1 // align correction (different font from above)
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
font.pixelSize: 20
|
||||||
|
font.weight: Font.DemiBold
|
||||||
|
color: iconBack.color
|
||||||
|
text: "Repl"
|
||||||
|
visible: (Qt.platform.os === "ios")
|
||||||
|
}
|
||||||
|
|
||||||
|
implicitWidth: 90
|
||||||
|
color: menuBack.color
|
||||||
|
}
|
||||||
|
|
||||||
|
onPressed: Lisp.call("dialogs:exited")
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: label
|
||||||
|
anchors.centerIn: parent
|
||||||
|
font.pixelSize: 20
|
||||||
|
font.weight: Font.DemiBold
|
||||||
|
}
|
||||||
|
}
|
||||||
13
examples/Qt6/cl-repl/qml/ext/MenuButton.qml
Normal file
13
examples/Qt6/cl-repl/qml/ext/MenuButton.qml
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Controls.Basic
|
||||||
|
|
||||||
|
Button {
|
||||||
|
width: main.small ? 30 : 42
|
||||||
|
height: width
|
||||||
|
font.family: fontAwesome.name
|
||||||
|
font.pixelSize: main.small ? 20 : 28
|
||||||
|
focusPolicy: Qt.NoFocus
|
||||||
|
|
||||||
|
onPressed: Lisp.call(this, "editor:button-pressed")
|
||||||
|
}
|
||||||
16
examples/Qt6/cl-repl/qml/ext/ParenButton.qml
Normal file
16
examples/Qt6/cl-repl/qml/ext/ParenButton.qml
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Controls.Basic
|
||||||
|
|
||||||
|
Button {
|
||||||
|
id: parenButton
|
||||||
|
width: 1.4 * (main.small ? 35 : 55)
|
||||||
|
icon.width: width / 1.4
|
||||||
|
icon.height: height / 1.4
|
||||||
|
height: width
|
||||||
|
focusPolicy: Qt.NoFocus
|
||||||
|
flat: true
|
||||||
|
opacity: 0.12
|
||||||
|
|
||||||
|
onPressed: Lisp.call(this, "editor:button-pressed")
|
||||||
|
}
|
||||||
50
examples/Qt6/cl-repl/qml/ext/QueryDialog.qml
Normal file
50
examples/Qt6/cl-repl/qml/ext/QueryDialog.qml
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Controls.Basic
|
||||||
|
import "." as Ext
|
||||||
|
|
||||||
|
Popup {
|
||||||
|
id: popup
|
||||||
|
x: 4
|
||||||
|
y: x
|
||||||
|
width: parent.width - 2 * x
|
||||||
|
height: queryInput.height + text.height + 24
|
||||||
|
closePolicy: Popup.NoAutoClose
|
||||||
|
|
||||||
|
onVisibleChanged: main.enabled = !visible
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: queryDialog
|
||||||
|
objectName: "query_dialog"
|
||||||
|
anchors.fill: parent
|
||||||
|
color: "#f0f0f0"
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: column
|
||||||
|
width: parent.width
|
||||||
|
|
||||||
|
TextField {
|
||||||
|
id: queryInput
|
||||||
|
objectName: "query_input"
|
||||||
|
width: parent.width
|
||||||
|
font.family: "Hack"
|
||||||
|
font.pixelSize: 18
|
||||||
|
inputMethodHints: Qt.ImhNoAutoUppercase | Qt.ImhNoPredictiveText
|
||||||
|
|
||||||
|
onAccepted: {
|
||||||
|
popup.close()
|
||||||
|
Lisp.call("dialogs:exited")
|
||||||
|
Lisp.call("editor:ensure-output-visible")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: text
|
||||||
|
objectName: "query_text"
|
||||||
|
width: parent.width
|
||||||
|
padding: 8
|
||||||
|
font.pixelSize: 18
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
33
examples/Qt6/cl-repl/qml/ext/ScrollBar.qml
Normal file
33
examples/Qt6/cl-repl/qml/ext/ScrollBar.qml
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
// This is a modified version taken from the QML sources
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Controls.Basic
|
||||||
|
|
||||||
|
ScrollBar {
|
||||||
|
id: control
|
||||||
|
orientation: Qt.Vertical
|
||||||
|
|
||||||
|
contentItem: Rectangle {
|
||||||
|
implicitWidth: 12
|
||||||
|
implicitHeight: 100
|
||||||
|
radius: width / 2
|
||||||
|
color: control.pressed ? "#202020" : "#909090"
|
||||||
|
opacity: 0.0
|
||||||
|
|
||||||
|
states: State {
|
||||||
|
name: "active"
|
||||||
|
when: (control.active && control.size < 1.0)
|
||||||
|
PropertyChanges { target: control.contentItem; opacity: 0.75 }
|
||||||
|
}
|
||||||
|
|
||||||
|
transitions: Transition {
|
||||||
|
from: "active"
|
||||||
|
SequentialAnimation {
|
||||||
|
PauseAnimation { duration: 450 }
|
||||||
|
NumberAnimation { target: control.contentItem; duration: 200; property: "opacity"; to: 0.0 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
11
examples/Qt6/cl-repl/qml/ext/TextField.qml
Normal file
11
examples/Qt6/cl-repl/qml/ext/TextField.qml
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Controls.Basic
|
||||||
|
|
||||||
|
TextField {
|
||||||
|
font.pixelSize: 18
|
||||||
|
palette {
|
||||||
|
highlight: "#007aff"
|
||||||
|
highlightedText: "white"
|
||||||
|
}
|
||||||
|
}
|
||||||
12
examples/Qt6/cl-repl/qml/ext/dialogs/Confirm.qml
Normal file
12
examples/Qt6/cl-repl/qml/ext/dialogs/Confirm.qml
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Dialogs
|
||||||
|
|
||||||
|
MessageDialog {
|
||||||
|
title: "LQML"
|
||||||
|
buttons: MessageDialog.Save | MessageDialog.Cancel
|
||||||
|
|
||||||
|
property string callback
|
||||||
|
|
||||||
|
onAccepted: Lisp.call(callback, true)
|
||||||
|
onRejected: Lisp.call(callback, false)
|
||||||
|
}
|
||||||
24
examples/Qt6/cl-repl/qml/ext/dialogs/ConfirmMobile.qml
Normal file
24
examples/Qt6/cl-repl/qml/ext/dialogs/ConfirmMobile.qml
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Controls.Basic
|
||||||
|
|
||||||
|
Dialog {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
title: "Confirm"
|
||||||
|
font.pixelSize: 18
|
||||||
|
modal: true
|
||||||
|
standardButtons: Dialog.Save | Dialog.Cancel
|
||||||
|
|
||||||
|
property alias text: message.text
|
||||||
|
property string callback
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: message
|
||||||
|
width: parent.width // without width word wrap won't work
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
font.pixelSize: 18
|
||||||
|
}
|
||||||
|
|
||||||
|
onAccepted: Lisp.call(callback, true)
|
||||||
|
onRejected: Lisp.call(callback, false)
|
||||||
|
}
|
||||||
36
examples/Qt6/cl-repl/qml/ext/dialogs/Dialogs.qml
Normal file
36
examples/Qt6/cl-repl/qml/ext/dialogs/Dialogs.qml
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
import QtQuick
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: dialogs
|
||||||
|
objectName: "dialogs"
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
id: loader
|
||||||
|
anchors.centerIn: parent
|
||||||
|
}
|
||||||
|
|
||||||
|
function message(text) {
|
||||||
|
if (Lisp.call("qml:mobile-p")) {
|
||||||
|
loader.source = "MessageMobile.qml"
|
||||||
|
} else {
|
||||||
|
loader.source = "Message.qml"
|
||||||
|
}
|
||||||
|
loader.item.text = text
|
||||||
|
main.showKeyboard(false)
|
||||||
|
loader.item.open()
|
||||||
|
}
|
||||||
|
|
||||||
|
function confirm(title, text, callback) {
|
||||||
|
if (Lisp.call("qml:mobile-p")) {
|
||||||
|
loader.source = "ConfirmMobile.qml"
|
||||||
|
} else {
|
||||||
|
loader.source = "Confirm.qml"
|
||||||
|
}
|
||||||
|
loader.item.title = title
|
||||||
|
loader.item.text = text
|
||||||
|
loader.item.callback = callback
|
||||||
|
main.showKeyboard(false)
|
||||||
|
loader.item.open()
|
||||||
|
}
|
||||||
|
}
|
||||||
7
examples/Qt6/cl-repl/qml/ext/dialogs/Message.qml
Normal file
7
examples/Qt6/cl-repl/qml/ext/dialogs/Message.qml
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Dialogs
|
||||||
|
|
||||||
|
MessageDialog {
|
||||||
|
title: "Info"
|
||||||
|
buttons: MessageDialog.Ok
|
||||||
|
}
|
||||||
20
examples/Qt6/cl-repl/qml/ext/dialogs/MessageMobile.qml
Normal file
20
examples/Qt6/cl-repl/qml/ext/dialogs/MessageMobile.qml
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Controls.Basic
|
||||||
|
|
||||||
|
Dialog {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
title: "Info"
|
||||||
|
font.pixelSize: 18
|
||||||
|
modal: true
|
||||||
|
standardButtons: Dialog.Ok
|
||||||
|
|
||||||
|
property alias text: message.text
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: message
|
||||||
|
width: parent.width // without width word wrap won't work
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
font.pixelSize: 18
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
examples/Qt6/cl-repl/qml/fonts/Hack-Bold.ttf
Normal file
BIN
examples/Qt6/cl-repl/qml/fonts/Hack-Bold.ttf
Normal file
Binary file not shown.
BIN
examples/Qt6/cl-repl/qml/fonts/Hack-Regular.ttf
Normal file
BIN
examples/Qt6/cl-repl/qml/fonts/Hack-Regular.ttf
Normal file
Binary file not shown.
BIN
examples/Qt6/cl-repl/qml/fonts/fontawesome-webfont.ttf
Normal file
BIN
examples/Qt6/cl-repl/qml/fonts/fontawesome-webfont.ttf
Normal file
Binary file not shown.
BIN
examples/Qt6/cl-repl/qml/img/paren-close.png
Normal file
BIN
examples/Qt6/cl-repl/qml/img/paren-close.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.3 KiB |
BIN
examples/Qt6/cl-repl/qml/img/paren-open.png
Normal file
BIN
examples/Qt6/cl-repl/qml/img/paren-open.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.4 KiB |
655
examples/Qt6/cl-repl/qml/main.qml
Normal file
655
examples/Qt6/cl-repl/qml/main.qml
Normal file
|
|
@ -0,0 +1,655 @@
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Controls.Basic
|
||||||
|
import QtQuick.Window
|
||||||
|
import 'ext/' as Ext
|
||||||
|
import 'ext/dialogs' as Dlg
|
||||||
|
|
||||||
|
StackView {
|
||||||
|
id: main
|
||||||
|
objectName: "main"
|
||||||
|
width: 800 // alternatively: Screen.desktopAvailableWidth
|
||||||
|
height: 600 // alternatively: Screen.desktopAvailableHeight
|
||||||
|
initialItem: mainRect
|
||||||
|
|
||||||
|
property bool small: (Math.max(width, height) < 1000)
|
||||||
|
property bool skipEnsureVisible: false
|
||||||
|
property double editorHeight: 0.5 // preferred initial height (50%)
|
||||||
|
property string cursorColor: "blue"
|
||||||
|
|
||||||
|
function mainHeight() {
|
||||||
|
var h = Math.round(Qt.inputMethod.keyboardRectangle.y /
|
||||||
|
((Qt.platform.os === "android") ? Screen.devicePixelRatio : 1))
|
||||||
|
return (h === 0) ? main.height : h
|
||||||
|
}
|
||||||
|
|
||||||
|
function divideHeight(factor) { return (mainHeight() - rectCommand.height) * factor }
|
||||||
|
function isLandscape() { return (Screen.primaryOrientation === Qt.LandscapeOrientation) }
|
||||||
|
function keyboardVisible() { return Qt.inputMethod.visible }
|
||||||
|
function showKeyboard(show) { show ? Qt.inputMethod.show() : Qt.inputMethod.hide() }
|
||||||
|
|
||||||
|
// show/hide dialogs
|
||||||
|
|
||||||
|
function pushDialog(name) {
|
||||||
|
switch (name) {
|
||||||
|
case "query": dialogQuery.open(); break
|
||||||
|
case "file": main.push(dialogFile); break
|
||||||
|
case "debug": main.push(dialogDebug); break
|
||||||
|
case "help": main.push(dialogHelp); break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function popDialog() { main.pop() }
|
||||||
|
|
||||||
|
Screen.onOrientationChanged: {
|
||||||
|
Lisp.call("editor:orientation-changed", Screen.orientation)
|
||||||
|
}
|
||||||
|
|
||||||
|
Keys.onPressed: (event) => {
|
||||||
|
if (event.key === Qt.Key_Back) {
|
||||||
|
event.accepted = true
|
||||||
|
Lisp.call("editor:back-pressed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// custom transition animations
|
||||||
|
|
||||||
|
pushEnter: Transition {
|
||||||
|
ParallelAnimation {
|
||||||
|
OpacityAnimator {
|
||||||
|
from: 0
|
||||||
|
to: 1
|
||||||
|
easing.type: Easing.OutQuart
|
||||||
|
duration: 300
|
||||||
|
}
|
||||||
|
XAnimator {
|
||||||
|
from: width / 3
|
||||||
|
to: 0
|
||||||
|
easing.type: Easing.OutQuart
|
||||||
|
duration: 300
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pushExit: Transition {
|
||||||
|
OpacityAnimator {
|
||||||
|
from: 1
|
||||||
|
to: 0
|
||||||
|
duration: 300
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
popEnter: Transition {
|
||||||
|
OpacityAnimator {
|
||||||
|
from: 0
|
||||||
|
to: 1
|
||||||
|
duration: 300
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
popExit: Transition {
|
||||||
|
ParallelAnimation {
|
||||||
|
OpacityAnimator {
|
||||||
|
from: 1
|
||||||
|
to: 0
|
||||||
|
easing.type: Easing.InQuart
|
||||||
|
duration: 300
|
||||||
|
}
|
||||||
|
XAnimator {
|
||||||
|
from: 0
|
||||||
|
to: width / 3
|
||||||
|
easing.type: Easing.InQuart
|
||||||
|
duration: 300
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// delay timer
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: timer
|
||||||
|
}
|
||||||
|
|
||||||
|
function delay(milliseconds, callback) {
|
||||||
|
timer.interval = milliseconds
|
||||||
|
timer.triggered.connect(callback)
|
||||||
|
timer.start()
|
||||||
|
}
|
||||||
|
|
||||||
|
function later(callback) {
|
||||||
|
delay(50, callback)
|
||||||
|
}
|
||||||
|
|
||||||
|
// fonts (must stay here, before using them below)
|
||||||
|
|
||||||
|
FontLoader { id: fontHack; source: "fonts/Hack-Regular.ttf" } // code
|
||||||
|
FontLoader { id: fontHackBold; source: "fonts/Hack-Bold.ttf" }
|
||||||
|
FontLoader { id: fontAwesome; source: "fonts/fontawesome-webfont.ttf" } // icons
|
||||||
|
|
||||||
|
// items
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: mainRect
|
||||||
|
|
||||||
|
SplitView {
|
||||||
|
id: splitView
|
||||||
|
anchors.fill: parent
|
||||||
|
orientation: Qt.Vertical
|
||||||
|
|
||||||
|
property double handleHeight: 10
|
||||||
|
|
||||||
|
handle: Rectangle {
|
||||||
|
implicitHeight: splitView.handleHeight
|
||||||
|
color: SplitHandle.pressed ? Qt.darker(rectOutput.color) : rectOutput.color
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: rectEdit
|
||||||
|
objectName: "rect_edit"
|
||||||
|
width: main.width
|
||||||
|
SplitView.preferredHeight: divideHeight(editorHeight)
|
||||||
|
|
||||||
|
Ext.Flickable {
|
||||||
|
id: flickEdit
|
||||||
|
objectName: "flick_edit"
|
||||||
|
anchors.fill: parent
|
||||||
|
contentWidth: edit.paintedWidth
|
||||||
|
contentHeight: edit.paintedHeight
|
||||||
|
|
||||||
|
TextEdit {
|
||||||
|
id: edit
|
||||||
|
objectName: "edit"
|
||||||
|
width: flickEdit.width
|
||||||
|
height: flickEdit.height
|
||||||
|
leftPadding: 2
|
||||||
|
font.family: "Hack"
|
||||||
|
font.pixelSize: 18
|
||||||
|
selectionColor: "firebrick"
|
||||||
|
inputMethodHints: Qt.ImhNoAutoUppercase | Qt.ImhNoPredictiveText | Qt.ImhNoTextHandles | Qt.ImhNoEditMenu
|
||||||
|
cursorDelegate: cursor
|
||||||
|
|
||||||
|
Keys.onTabPressed: command.forceActiveFocus()
|
||||||
|
|
||||||
|
onCursorRectangleChanged: flickEdit.ensureVisible(cursorRectangle)
|
||||||
|
|
||||||
|
Component.onCompleted: later(function() {
|
||||||
|
Lisp.call("editor:set-text-document", objectName, textDocument)
|
||||||
|
})
|
||||||
|
|
||||||
|
// for external keyboard
|
||||||
|
Shortcut {
|
||||||
|
sequence: "Ctrl+E" // E for Expression
|
||||||
|
onActivated: Lisp.call("editor:select-expression")
|
||||||
|
}
|
||||||
|
Shortcut {
|
||||||
|
sequence: "Ctrl+L" // L for Lambda
|
||||||
|
onActivated: Lisp.call("editor:eval-single-expression")
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
width: Math.max(rectEdit.width, edit.paintedWidth)
|
||||||
|
height: Math.max(rectEdit.height, edit.paintedHeight)
|
||||||
|
|
||||||
|
onPressed: (mouse) => {
|
||||||
|
// seems necessary to consistently move cursor by tapping
|
||||||
|
edit.forceActiveFocus()
|
||||||
|
edit.cursorPosition = edit.positionAt(mouse.x, mouse.y)
|
||||||
|
Qt.inputMethod.show() // needed for edge case (since we have 2 input fields)
|
||||||
|
Lisp.call("editor:set-focus-editor", edit.objectName)
|
||||||
|
}
|
||||||
|
|
||||||
|
onPressAndHold: Lisp.call("editor:copy-paste", edit.cursorPosition)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
width: parent.width
|
||||||
|
height: rectCommand.height + rectOutput.height
|
||||||
|
SplitView.fillHeight: false // see comment in rectOutput
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: rectCommand
|
||||||
|
objectName: "rect_command"
|
||||||
|
width: parent.width
|
||||||
|
height: command.font.pixelSize + 11
|
||||||
|
border.width: 2
|
||||||
|
border.color: command.focus ? "#0066ff" : "lightgray"
|
||||||
|
|
||||||
|
Ext.Flickable {
|
||||||
|
id: flickCommand
|
||||||
|
objectName: "flick_command"
|
||||||
|
anchors.fill: parent
|
||||||
|
contentWidth: command.paintedWidth
|
||||||
|
contentHeight: command.paintedHeight
|
||||||
|
|
||||||
|
TextEdit {
|
||||||
|
id: command
|
||||||
|
objectName: "command"
|
||||||
|
width: flickCommand.width
|
||||||
|
height: flickCommand.height
|
||||||
|
padding: 4
|
||||||
|
font.family: "Hack"
|
||||||
|
font.pixelSize: 18
|
||||||
|
selectionColor: "firebrick"
|
||||||
|
inputMethodHints: Qt.ImhNoAutoUppercase | Qt.ImhNoPredictiveText | Qt.ImhNoTextHandles | Qt.ImhNoEditMenu
|
||||||
|
cursorDelegate: cursor
|
||||||
|
focus: true
|
||||||
|
|
||||||
|
Keys.onUpPressed: Lisp.call("editor:history-move", "back")
|
||||||
|
Keys.onDownPressed: Lisp.call("editor:history-move", "forward")
|
||||||
|
Keys.onTabPressed: edit.forceActiveFocus()
|
||||||
|
|
||||||
|
onCursorRectangleChanged: flickCommand.ensureVisible(cursorRectangle)
|
||||||
|
|
||||||
|
Component.onCompleted: later(function() {
|
||||||
|
Lisp.call("editor:set-text-document", objectName, textDocument)
|
||||||
|
})
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
width: Math.max(rectCommand.width, command.paintedWidth)
|
||||||
|
height: Math.max(rectCommand.height, command.paintedHeight)
|
||||||
|
|
||||||
|
onPressed: {
|
||||||
|
// seems necessary to consistently move cursor by tapping
|
||||||
|
command.forceActiveFocus()
|
||||||
|
command.cursorPosition = command.positionAt(mouse.x, mouse.y)
|
||||||
|
Qt.inputMethod.show() // needed for edge case (since we have 2 input fields)
|
||||||
|
Lisp.call("editor:set-focus-editor", command.objectName)
|
||||||
|
Lisp.call("editor:ensure-output-visible")
|
||||||
|
}
|
||||||
|
|
||||||
|
onPressAndHold: Lisp.call("editor:copy-paste", command.cursorPosition)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: rectOutput
|
||||||
|
objectName: "rect_output"
|
||||||
|
width: main.width
|
||||||
|
// calculate manually (for virtual keyboard)
|
||||||
|
height: main.mainHeight() - rectEdit.height - rectCommand.height - splitView.handleHeight
|
||||||
|
|
||||||
|
ListView {
|
||||||
|
id: output
|
||||||
|
objectName: "output"
|
||||||
|
anchors.fill: parent
|
||||||
|
contentWidth: parent.width * 5
|
||||||
|
clip: true
|
||||||
|
model: outputModel
|
||||||
|
flickableDirection: Flickable.HorizontalAndVerticalFlick
|
||||||
|
|
||||||
|
property string fontFamily: "Hack"
|
||||||
|
property int fontSize: 18
|
||||||
|
|
||||||
|
delegate: Column {
|
||||||
|
Rectangle {
|
||||||
|
width: output.contentWidth
|
||||||
|
height: model.line ? 2 : 0
|
||||||
|
color: "#c0c0ff"
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
x: 2
|
||||||
|
padding: 2
|
||||||
|
textFormat: Text.PlainText
|
||||||
|
font.family: output.fontFamily
|
||||||
|
font.pixelSize: output.fontSize
|
||||||
|
text: model.richText ? "" : model.text
|
||||||
|
color: model.color
|
||||||
|
font.bold: model.bold
|
||||||
|
visible: !model.richText
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
x: 2
|
||||||
|
padding: 2
|
||||||
|
textFormat: Text.RichText
|
||||||
|
font.family: output.fontFamily
|
||||||
|
font.pixelSize: output.fontSize
|
||||||
|
text: model.richText ? model.text : ""
|
||||||
|
color: model.color
|
||||||
|
font.bold: model.bold
|
||||||
|
visible: model.richText
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
width: parent.paintedWidth
|
||||||
|
height: parent.paintedHeight
|
||||||
|
|
||||||
|
onPressed: {
|
||||||
|
// custom link handling, since 'onLinkActivated' does not work within a Flickable
|
||||||
|
var link = parent.linkAt(mouse.x, mouse.y)
|
||||||
|
if (link.length) {
|
||||||
|
Qt.openUrlExternally(link)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onFlickStarted: forceActiveFocus()
|
||||||
|
|
||||||
|
Component.onCompleted: later(function () {
|
||||||
|
Lisp.call("editor:delayed-ini")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
ListModel {
|
||||||
|
id: outputModel
|
||||||
|
objectName: "output_model"
|
||||||
|
|
||||||
|
function appendOutput(text) {
|
||||||
|
append(text)
|
||||||
|
output.contentX = 0
|
||||||
|
output.positionViewAtEnd()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ProgressBar {
|
||||||
|
objectName: "progress"
|
||||||
|
width: main.width
|
||||||
|
z: 1
|
||||||
|
indeterminate: true
|
||||||
|
enabled: visible
|
||||||
|
visible: false
|
||||||
|
}
|
||||||
|
|
||||||
|
// move history buttons
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: buttonsBottom
|
||||||
|
width: rowButtonsBottom.width
|
||||||
|
height: rowButtonsBottom.height
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
opacity: 0.7
|
||||||
|
visible: command.activeFocus
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: rowButtonsBottom
|
||||||
|
padding: 4
|
||||||
|
spacing: 6
|
||||||
|
|
||||||
|
Ext.MenuButton {
|
||||||
|
objectName: "history_back"
|
||||||
|
text: "\uf100"
|
||||||
|
}
|
||||||
|
Ext.MenuButton {
|
||||||
|
objectName: "history_forward"
|
||||||
|
text: "\uf101"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// paren buttons (above keyboard)
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
objectName: "rect_paren_buttons"
|
||||||
|
width: rowParens.width
|
||||||
|
height: rowParens.height
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
color: "transparent"
|
||||||
|
visible: Qt.inputMethod.visible
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: rowParens
|
||||||
|
padding: -parenOpen.width / 30
|
||||||
|
spacing: -parenOpen.width / 6
|
||||||
|
|
||||||
|
Ext.ParenButton {
|
||||||
|
id: parenOpen
|
||||||
|
objectName: "paren_open"
|
||||||
|
icon.source: "img/paren-open.png"
|
||||||
|
onClicked: Lisp.call("editor:insert", "(")
|
||||||
|
}
|
||||||
|
Ext.ParenButton {
|
||||||
|
objectName: "paren_close"
|
||||||
|
icon.source: "img/paren-close.png"
|
||||||
|
onClicked: Lisp.call("editor:insert", ")")
|
||||||
|
onPressAndHold: Lisp.call("editor:close-all-parens")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// arrow buttons (cursor movement)
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: rectArrows
|
||||||
|
objectName: "rect_arrows"
|
||||||
|
width: arrows.width + 20
|
||||||
|
height: width
|
||||||
|
anchors.right: rectOutput.right
|
||||||
|
anchors.bottom: rectOutput.bottom
|
||||||
|
color: "transparent"
|
||||||
|
visible: Qt.inputMethod.visible
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
onPressed: Lisp.call("editor:ensure-focus")
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: arrows
|
||||||
|
width: up.width * 3
|
||||||
|
height: width
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|
||||||
|
Ext.ArrowButton {
|
||||||
|
id: up
|
||||||
|
objectName: "up"
|
||||||
|
text: "\uf139"
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
Ext.ArrowButton {
|
||||||
|
objectName: "left"
|
||||||
|
text: "\uf137"
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
Ext.ArrowButton {
|
||||||
|
objectName: "right"
|
||||||
|
text: "\uf138"
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
anchors.right: parent.right
|
||||||
|
}
|
||||||
|
|
||||||
|
Ext.ArrowButton {
|
||||||
|
objectName: "down"
|
||||||
|
text: "\uf13a"
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: cursor
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: 2
|
||||||
|
color: main.cursorColor
|
||||||
|
visible: parent.activeFocus
|
||||||
|
|
||||||
|
SequentialAnimation on opacity {
|
||||||
|
running: true
|
||||||
|
loops: Animation.Infinite
|
||||||
|
|
||||||
|
NumberAnimation { to: 0; duration: 500; easing.type: "OutQuad" }
|
||||||
|
NumberAnimation { to: 1; duration: 500; easing.type: "InQuad" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: buttonsTop
|
||||||
|
objectName: "buttons_top"
|
||||||
|
y: -height // hidden
|
||||||
|
width: rowButtonsTop.width
|
||||||
|
height: rowButtonsTop.height
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
opacity: 0.7
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: rowButtonsTop
|
||||||
|
padding: 4
|
||||||
|
spacing: 6
|
||||||
|
|
||||||
|
Ext.MenuButton {
|
||||||
|
objectName: "undo"
|
||||||
|
text: "\uf0e2"
|
||||||
|
enabled: edit.canUndo
|
||||||
|
}
|
||||||
|
Ext.MenuButton {
|
||||||
|
objectName: "redo"
|
||||||
|
text: "\uf01e"
|
||||||
|
enabled: edit.canRedo
|
||||||
|
}
|
||||||
|
Ext.MenuButton {
|
||||||
|
objectName: "font_smaller"
|
||||||
|
text: "\uf010"
|
||||||
|
font.pixelSize: main.small ? 10 : 15
|
||||||
|
}
|
||||||
|
Ext.MenuButton {
|
||||||
|
objectName: "font_bigger"
|
||||||
|
text: "\uf00e"
|
||||||
|
font.pixelSize: main.small ? 16 : 25
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ext.MenuButton {
|
||||||
|
id: showMenu
|
||||||
|
objectName: "show_menu"
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: 4
|
||||||
|
y: 4
|
||||||
|
opacity: 0.7
|
||||||
|
text: "\uf142"
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
showButtonsTop.start()
|
||||||
|
showButtonsRight.start()
|
||||||
|
menuTimer.start()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: menuTimer
|
||||||
|
objectName: "menu_timer"
|
||||||
|
interval: 3000
|
||||||
|
onTriggered: {
|
||||||
|
if (buttonsTop.y === 0) {
|
||||||
|
hideButtonsTop.start()
|
||||||
|
hideButtonsRight.start()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: buttonsRight
|
||||||
|
objectName: "buttons_right"
|
||||||
|
x: -width // hidden
|
||||||
|
width: colButtonsRight.width
|
||||||
|
height: colButtonsRight.height
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: colButtonsRight
|
||||||
|
padding: 4
|
||||||
|
spacing: 6
|
||||||
|
|
||||||
|
Ext.Button {
|
||||||
|
objectName: "clear"
|
||||||
|
text: "\uf014"
|
||||||
|
}
|
||||||
|
Ext.Button {
|
||||||
|
objectName: "open_file"
|
||||||
|
text: "\uf115"
|
||||||
|
}
|
||||||
|
Ext.Button {
|
||||||
|
objectName: "save_file"
|
||||||
|
text: "\uf0c7"
|
||||||
|
}
|
||||||
|
Ext.Button {
|
||||||
|
objectName: "eval"
|
||||||
|
text: "\u03bb" // lambda
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// animations for showing/hiding editor menu buttons
|
||||||
|
|
||||||
|
NumberAnimation {
|
||||||
|
id: showButtonsTop
|
||||||
|
objectName: "show_buttons_top"
|
||||||
|
target: buttonsTop
|
||||||
|
property: "y"
|
||||||
|
from: -buttonsTop.height
|
||||||
|
to: 0
|
||||||
|
duration: 500
|
||||||
|
easing.type: Easing.OutExpo
|
||||||
|
}
|
||||||
|
|
||||||
|
NumberAnimation {
|
||||||
|
id: showButtonsRight
|
||||||
|
objectName: "show_buttons_right"
|
||||||
|
target: buttonsRight
|
||||||
|
property: "x"
|
||||||
|
from: buttonsRight.parent.width
|
||||||
|
to: buttonsRight.parent.width - buttonsRight.width
|
||||||
|
duration: 500
|
||||||
|
easing.type: Easing.OutExpo
|
||||||
|
}
|
||||||
|
|
||||||
|
NumberAnimation {
|
||||||
|
id: hideButtonsTop
|
||||||
|
target: buttonsTop
|
||||||
|
property: "y"
|
||||||
|
from: 0
|
||||||
|
to: -buttonsTop.height
|
||||||
|
duration: 500
|
||||||
|
easing.type: Easing.InExpo
|
||||||
|
}
|
||||||
|
|
||||||
|
NumberAnimation {
|
||||||
|
id: hideButtonsRight
|
||||||
|
target: buttonsRight
|
||||||
|
property: "x"
|
||||||
|
from: buttonsRight.parent.width - buttonsRight.width
|
||||||
|
to: buttonsRight.parent.width
|
||||||
|
duration: 500
|
||||||
|
easing.type: Easing.InExpo
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// custom font loader
|
||||||
|
|
||||||
|
function loadFont(file) {
|
||||||
|
var font = Qt.createQmlObject("import QtQuick 2.15; FontLoader { source: '" + file + "' }", main)
|
||||||
|
return font.name
|
||||||
|
}
|
||||||
|
|
||||||
|
// not visible dialog / menu instances
|
||||||
|
|
||||||
|
Ext.QueryDialog { id: dialogQuery }
|
||||||
|
Ext.FileBrowser { id: dialogFile; opacity: 0 }
|
||||||
|
Ext.DebugDialog { id: dialogDebug; opacity: 0 }
|
||||||
|
Ext.Help { id: dialogHelp; opacity: 0 }
|
||||||
|
|
||||||
|
Ext.ClipboardMenu {}
|
||||||
|
|
||||||
|
// modal dialogs
|
||||||
|
|
||||||
|
Dlg.Dialogs {}
|
||||||
|
|
||||||
|
// dynamic QML items
|
||||||
|
|
||||||
|
Ext.Dynamic {}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue