Add save+open to notebook, allow multiple windows, add translations, add welcome window
diff --git a/ide/qml/NbWindow.qml b/ide/qml/NbWindow.qml
new file mode 100644
index 0000000..5710c9e
--- /dev/null
+++ b/ide/qml/NbWindow.qml
@@ -0,0 +1,213 @@
+import QtQuick 2.5
+import QtQuick.Controls 2.15
+import QtQuick.Controls.Material 2.0
+import QtQuick.Layouts 1.3
+
+import sh.swisschili.REFAL 1.0
+
+ApplicationWindow {
+ id: root
+ width: 800
+ height: 800
+ title: "Refal Notebook — " + notebook.savePath
+ visible: true
+
+ Material.theme: Material.Light
+ Material.accent: Material.Orange
+
+ minimumWidth: column.implicitWidth
+
+ required property ApplicationWindow welcomeWindow
+
+ function openNotebook(path) {
+ notebook.open(path);
+ }
+
+ menuBar: MenuBar {
+ Menu {
+ title: qsTr("&File")
+
+ Action {
+ text: qsTr("&New")
+
+ onTriggered: {
+ welcomeWindow.openNotebook();
+ }
+ }
+
+ Action {
+ text: qsTr("&Save")
+ shortcut: "Ctrl+s"
+
+ onTriggered: {
+ notebook.save()
+ }
+ }
+
+ Action {
+ text: qsTr("&Open")
+ shortcut: "Ctrl+o"
+
+ onTriggered: {
+ notebook.open();
+ }
+ }
+ }
+
+ Menu {
+ title: qsTr("&View")
+
+ Action {
+ text: qsTr("&Welcome Window")
+ checkable: true
+
+ checked: welcomeWindow.visible
+
+ onTriggered: {
+ welcomeWindow.toggleVisible()
+ }
+ }
+
+ Action {
+ id: varInspector
+ text: qsTr("&Variable Inspector")
+
+ checkable: true
+ }
+ }
+
+ Menu {
+ title: qsTr("&Runtime")
+
+ Action {
+ text: qsTr("Run &Selected Cell")
+ shortcut: "Ctrl+Return"
+
+ onTriggered: {
+ if (codeEditor.currentItem !== null) {
+ notebook.runCell(codeEditor.currentItem.uuid)
+ }
+ }
+ }
+
+ Action {
+ text: qsTr("Run &All")
+ }
+
+ Action {
+ text: qsTr("&Reset Runtime State")
+ }
+ }
+ }
+
+ Notebook {
+ id: notebook
+
+ onSaveError: (message) =>
+ {
+ console.error(message)
+ }
+ }
+
+ ColumnLayout {
+ id: column
+ anchors.fill: parent
+
+ SplitView {
+ id: split
+ Layout.fillHeight: true
+ Layout.fillWidth: true
+ orientation: Qt.Horizontal
+
+ ListView {
+ id: codeEditor
+ SplitView.fillWidth: true
+ SplitView.minimumWidth: 400
+ model: notebook.cellModel
+ clip: true
+
+ spacing: 5
+
+ header: ColumnLayout {
+ width: codeEditor.width
+
+ Pane {
+ Layout.bottomMargin: 0
+
+ ColumnLayout {
+ Label {
+ font.pointSize: 18
+ text: qsTr("Notebook")
+ }
+
+ Label {
+ visible: codeEditor.count === 0
+
+ text: qsTr("Looks like you haven't created any cells yet. Click the + button below to create one.")
+ }
+ }
+ }
+
+ InsertRow {
+ onInsertClicked: notebook.cellModel.insertCellBefore(0)
+ }
+
+ Item {
+ height: 5 // JANK!
+ }
+ }
+
+ delegate: NotebookCell {
+ id: notebookCell
+
+ required property var model
+ required property var index
+ required property var uuid
+
+ width: codeEditor.width
+
+ code: model.code
+ result: model.result.trim()
+ status: model.status
+ resultType: model.resultType
+ cellActive: codeEditor.currentIndex === index
+
+ onCodeEditingFinished: code => model.code = code
+
+ onInsertBelowClicked: {
+ console.info(index);
+ notebook.cellModel.insertCellBefore(index + 1);
+ }
+
+ onRunClicked: {
+ notebook.runCell(uuid)
+ }
+
+ onCellFocused: {
+ codeEditor.currentIndex = index
+ }
+
+ onDeleteClicked: {
+ notebook.cellModel.deleteCellAt(index)
+ }
+
+ onCellUnfocused: {
+ codeEditor.currentIndex = -1
+ }
+ }
+ }
+
+ Item {
+ id: variables
+ SplitView.minimumWidth: 240
+
+ visible: varInspector.checked
+
+ Label {
+ anchors.centerIn: parent
+ text: qsTr("Variables")
+ }
+ }
+ }
+ }
+}
diff --git a/ide/qml/NotebookCell.qml b/ide/qml/NotebookCell.qml
index 24eaf17..7653dd7 100644
--- a/ide/qml/NotebookCell.qml
+++ b/ide/qml/NotebookCell.qml
@@ -90,7 +90,7 @@
selectByMouse: true
wrapMode: TextEdit.WrapAtWordBoundaryOrAnywhere
- placeholderText: "Write some code..."
+ placeholderText: qsTr("Write some code...")
Keys.onTabPressed: {
var pos = cursorPosition + 4
@@ -134,7 +134,7 @@
MenuItem {
icon.source: "qrc:///icons/trash.svg"
icon.color: Material.color(Material.Red)
- text: "Delete"
+ text: qsTr("Delete")
onClicked: root.deleteClicked()
}
diff --git a/ide/qml/RecentNotebook.qml b/ide/qml/RecentNotebook.qml
new file mode 100644
index 0000000..d0b1120
--- /dev/null
+++ b/ide/qml/RecentNotebook.qml
@@ -0,0 +1,39 @@
+import QtQuick 2.0
+import QtQuick.Layouts 1.12
+import QtQuick.Controls 2.0
+
+RowLayout {
+ id: root
+
+ property string name: "Hello.refnb"
+ property alias containsMouse: mouseArea.containsMouse
+ signal clicked()
+
+ Image {
+ id: nbIcon
+ width: 100
+ height: 100
+ source: "qrc:///icons/document.svg"
+ fillMode: Image.PreserveAspectFit
+ }
+
+ Label {
+ text: name
+ Layout.fillWidth: true
+
+ MouseArea {
+ id: mouseArea
+
+ anchors.fill: parent
+ hoverEnabled: true
+
+ onClicked: root.clicked()
+ }
+ }
+}
+
+/*##^##
+Designer {
+ D{i:0;autoSize:true;height:24;width:300}
+}
+##^##*/
diff --git a/ide/qml/Tip.qml b/ide/qml/Tip.qml
new file mode 100644
index 0000000..ba6084b
--- /dev/null
+++ b/ide/qml/Tip.qml
@@ -0,0 +1,33 @@
+import QtQuick 2.0
+import QtQuick.Layouts 1.12
+import QtQuick.Controls 2.0
+
+RowLayout {
+ id: root
+
+ property string title: "title"
+ property string url: "https://swisschili.sh"
+ property alias containsMouse: mouseArea.containsMouse
+
+ Image {
+ id: nbIcon
+ width: 100
+ height: 100
+ source: "qrc:///icons/book.svg"
+ fillMode: Image.PreserveAspectFit
+ }
+
+ Label {
+ text: title
+ Layout.fillWidth: true
+
+ MouseArea {
+ id: mouseArea
+
+ anchors.fill: parent
+ hoverEnabled: true
+
+ onClicked: Qt.openUrlExternally(url)
+ }
+ }
+}
diff --git a/ide/qml/main.qml b/ide/qml/main.qml
index ab86efd..23f8926 100644
--- a/ide/qml/main.qml
+++ b/ide/qml/main.qml
@@ -1,178 +1,186 @@
-import QtQuick 2.5
+import QtQuick 2.0
import QtQuick.Controls 2.15
import QtQuick.Controls.Material 2.0
-import QtQuick.Layouts 1.3
-
-import sh.swisschili.REFAL 1.0
+import QtQuick.Layouts 1.11
ApplicationWindow {
id: root
- width: 1080
- height: 720
- title: "Refal Notebook -- " + notebook.savePath
- visible: true
+
+ title: "REFAL Studio"
+
+ width: 680
+ height: 360
+
+ minimumWidth: 680
+ minimumHeight: 360
Material.theme: Material.Light
Material.accent: Material.Orange
- menuBar: MenuBar {
- Menu {
- title: qsTr("&File")
+ visible: true
- Action {
- text: "&Save"
- shortcut: "Ctrl+s"
+ function openNotebook(path=null) {
+ let NbWindow = Qt.createComponent("qrc:///qml/NbWindow.qml");
+ let window = NbWindow.createObject(null, {welcomeWindow: root});
- onTriggered: {
- notebook.save()
- }
- }
- }
-
- Menu {
- title: qsTr("&Runtime")
-
- Action {
- text: qsTr("Run &Selected Cell")
- shortcut: "Ctrl+Return"
-
- onTriggered: {
- if (codeEditor.currentItem !== null) {
- notebook.runCell(codeEditor.currentItem.uuid)
- }
- }
- }
- }
- }
-
- Notebook {
- id: notebook
-
- onSaveError: (message) =>
+ if (path !== null)
{
- console.error(message)
+ window.openNotebook(path)
}
}
- ColumnLayout {
- id: column
- anchors.fill: parent
+ function toggleVisible() {
+ if (visible)
+ hide();
+ else
+ show();
+ }
- TabBar {
- id: bar
+ Label {
+ id: textRefal
+ text: qsTr("REFAL")
+ anchors.left: parent.left
+ anchors.top: parent.top
+ font.pixelSize: 36
+ anchors.leftMargin: 36
+ anchors.topMargin: 29
+ font.weight: Font.Black
+ font.bold: true
+ font.italic: false
+ }
- Layout.fillWidth: true
+ Label {
+ id: textStudio
+ y: 29
+ text: qsTr("Studio")
+ anchors.verticalCenter: textRefal.verticalCenter
+ anchors.left: textRefal.right
+ font.pixelSize: 36
+ anchors.leftMargin: 6
+ font.bold: false
+ font.italic: false
+ font.weight: Font.Medium
+ color: Material.color(Material.Orange)
+ }
- TabButton {
- text: "Notebook"
- width: implicitWidth
+ Rectangle {
+ id: sepRect
+ x: 364
+ width: 1
+ color: "#cecece"
+ anchors.top: parent.top
+ anchors.bottom: parent.bottom
+ anchors.bottomMargin: 14
+ anchors.topMargin: 78
+ anchors.horizontalCenter: parent.horizontalCenter
+ }
+
+ Flickable {
+ id: notebooksFlick
+ anchors.left: textRefal.left
+ anchors.right: sepRect.left
+ anchors.top: textRefal.bottom
+ anchors.bottom: parent.bottom
+ anchors.leftMargin: -8
+ anchors.rightMargin: 16
+ anchors.bottomMargin: 16
+ anchors.topMargin: 6
+ clip: true
+ flickableDirection: Flickable.VerticalFlick
+
+ ColumnLayout {
+ id: notebooksCol
+ spacing: 12
+
+ RowLayout {
+ id: nbBtnsRow
+ spacing: 12
+ Layout.alignment: Qt.AlignLeft | Qt.AlignTop
+ Layout.fillWidth: true
+ Layout.fillHeight: false
+ Layout.leftMargin: 8
+ Layout.rightMargin: 8
+
+ Button {
+ id: newNotebookBtn
+ text: qsTr("New Notebook")
+ font.bold: true
+ highlighted: true
+
+ onClicked: {
+ root.openNotebook();
+ }
+ }
+
+ Button {
+ id: openNotebookBtn
+ text: qsTr("Open Existing")
+ }
}
- TabButton {
- text: "Another Notebook"
- width: implicitWidth
+ Repeater {
+ id: notebooksList
+
+ Layout.alignment: Qt.AlignLeft | Qt.AlignTop
+ Layout.fillWidth: true
+ Layout.fillHeight: false
+
+ model: [
+ // "~/Documents/Hello.refnb", "~/Downloads/stuff/Goodbye.refnb", "/home/ch/dev/REFAL/build/test.refnb"
+ ]
+
+ delegate: RecentNotebook {
+ Layout.leftMargin: 8
+ Layout.rightMargin: 8
+
+ name: modelData.split("/").pop()
+
+ ToolTip.text: modelData
+ ToolTip.visible: containsMouse
+ ToolTip.delay: 1000
+
+ onClicked: root.openNotebook(modelData)
+ }
}
- TabButton {
- text: "Testing"
- width: implicitWidth
+ Label {
+ Layout.leftMargin: 8
+ Layout.rightMargin: 8
+
+ visible: notebooksList.count == 0
+ text: qsTr("Your recent notebooks will appear here")
}
}
+ }
- SplitView {
- id: split
- Layout.fillHeight: true
+ ListView {
+ id: tipsList
+ anchors.left: sepRect.right
+ anchors.right: parent.right
+ anchors.top: textRefal.bottom
+ anchors.bottom: parent.bottom
+ anchors.bottomMargin: 16
+ anchors.topMargin: 6
+ anchors.leftMargin: 16
+ anchors.rightMargin: 36
+ spacing: 12
+
+ model: [
+ {url: "https://wiki.swisschili.sh/wiki/REFAL", title: "REFAL Studio Wiki"},
+ {url: "https://wiki.swisschili.sh/wiki/REFAL/Cookbook", title: "REFAL Cookbook"},
+ {url: "http://refal.botik.ru/book/html/", title: "REFAL-5 Programming Guide (en)"},
+ {url: "http://refal.net/rf5_frm.htm", title: "REFAL-5 Programming Guide (ru)"}
+ ]
+
+ delegate: Tip {
Layout.fillWidth: true
- orientation: Qt.Horizontal
- ListView {
- id: codeEditor
- SplitView.fillWidth: true
- SplitView.minimumWidth: 400
- model: notebook.cellModel
- clip: true
+ url: modelData.url
+ title: modelData.title
- spacing: 5
-
- header: ColumnLayout {
- width: codeEditor.width
-
- Pane {
- Layout.bottomMargin: 0
-
- ColumnLayout {
- Label {
- font.pointSize: 18
- text: "Notebook"
- }
-
- Label {
- visible: codeEditor.count === 0
-
- text: "Looks like you haven't created any cells yet. Click the + button below to create one."
- }
- }
- }
-
- InsertRow {
- onInsertClicked: notebook.cellModel.insertCellBefore(0)
- }
-
- Item {
- height: 5 // JANK!
- }
- }
-
- delegate: NotebookCell {
- id: notebookCell
-
- required property var model
- required property var index
- required property var uuid
-
- width: codeEditor.width
-
- code: model.code
- result: model.result.trim()
- status: model.status
- resultType: model.resultType
- cellActive: codeEditor.currentIndex === index
-
- onCodeEditingFinished: code => model.code = code
-
- onInsertBelowClicked: {
- console.info(index);
- notebook.cellModel.insertCellBefore(index + 1);
- }
-
- onRunClicked: {
- console.info("Cell run clicked")
- notebook.runCell(uuid)
- }
-
- onCellFocused: {
- codeEditor.currentIndex = index
- }
-
- onDeleteClicked: {
- notebook.cellModel.deleteCellAt(index)
- }
-
- onCellUnfocused: {
- codeEditor.currentIndex = -1
- }
- }
- }
-
- Item {
- id: variables
- SplitView.minimumWidth: 240
-
- Label {
- anchors.centerIn: parent
- text: "Vars"
- }
- }
+ ToolTip.text: modelData.url
+ ToolTip.visible: containsMouse
+ ToolTip.delay: 1000
}
}
}