Add basic IDE UI
diff --git a/REFAL.pro b/REFAL.pro
new file mode 100644
index 0000000..b4fa9c5
--- /dev/null
+++ b/REFAL.pro
@@ -0,0 +1,41 @@
+QT += core
+
+CONFIG += c++14
+
+# You can make your code fail to compile if it uses deprecated APIs.
+# In order to do so, uncomment the following line.
+#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
+
+SOURCES += \
+ AstNode.cpp \
+ Evaluator.cpp \
+ Function.cpp \
+ main.cpp \
+ Matcher.cpp \
+ Parser.cpp \
+ PPrint.cpp \
+ Repl.cpp \
+ StdLib.cpp \
+ Token.cpp \
+ VarContext.cpp
+
+HEADERS += \
+ AstNode.h \
+ Evaluator.h \
+ Function.h \
+ Matcher.h \
+ Parser.h \
+ PPrint.h \
+ Repl.h \
+ StdLib.h \
+ Token.h \
+ VarContext.h
+
+include(ide/IDE.pri)
+
+# Default rules for deployment.
+qnx: target.path = /tmp/$${TARGET}/bin
+else: unix:!android: target.path = /opt/$${TARGET}/bin
+!isEmpty(target.path): INSTALLS += target
+
+LIBS += -lreadline
diff --git a/ide/IDE.pri b/ide/IDE.pri
new file mode 100644
index 0000000..e430568
--- /dev/null
+++ b/ide/IDE.pri
@@ -0,0 +1,23 @@
+QT += quick quickcontrols2
+QT -= core
+
+CONFIG +=
+
+QML_IMPORT_NAME = sh.swisschili.REFAL
+QML_IMPORT_MAJOR_VERSION = 1
+QML_IMPORT_MINOR_VERSION = 0
+
+# Additional import path used to resolve QML modules in Qt Creator's code model
+QML_IMPORT_PATH =
+
+# Additional import path used to resolve QML modules just for Qt Quick Designer
+QML_DESIGNER_IMPORT_PATH =
+
+SOURCES += \
+ $$PWD/IdeMain.cpp
+
+HEADERS += \
+ $$PWD/IdeMain.h
+
+RESOURCES += \
+ $$PWD/resources.qrc
diff --git a/ide/IdeMain.cpp b/ide/IdeMain.cpp
new file mode 100644
index 0000000..a8f918c
--- /dev/null
+++ b/ide/IdeMain.cpp
@@ -0,0 +1,25 @@
+#include "IdeMain.h"
+
+#include <QQmlApplicationEngine>
+#include <QQuickStyle>
+
+int ideMain(QGuiApplication *app)
+{
+ QQmlApplicationEngine engine;
+
+ // This is done implicitly now.
+ // registerTypes(&engine);
+
+ QQuickStyle::setStyle("Material");
+
+ const QUrl url(QStringLiteral("qrc:/qml/main.qml"));
+ QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
+ app, [url](QObject *obj, const QUrl &objUrl)
+ {
+ if (!obj && url == objUrl)
+ QCoreApplication::exit(-1);
+ }, Qt::QueuedConnection);
+ engine.load(url);
+
+ return app->exec();
+}
diff --git a/ide/IdeMain.h b/ide/IdeMain.h
new file mode 100644
index 0000000..6b911ba
--- /dev/null
+++ b/ide/IdeMain.h
@@ -0,0 +1,7 @@
+#pragma once
+
+#include <QGuiApplication>
+
+using Application = QGuiApplication;
+
+int ideMain(QGuiApplication *app);
diff --git a/ide/icons/add.svg b/ide/icons/add.svg
new file mode 100644
index 0000000..00ffc63
--- /dev/null
+++ b/ide/icons/add.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M416 277.333H277.333V416h-42.666V277.333H96v-42.666h138.667V96h42.666v138.667H416v42.666z"/></svg>
\ No newline at end of file
diff --git a/ide/icons/play-circle.svg b/ide/icons/play-circle.svg
new file mode 100644
index 0000000..ff8963e
--- /dev/null
+++ b/ide/icons/play-circle.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M256 48C141.2 48 48 141.2 48 256s93.2 208 208 208 208-93.2 208-208S370.8 48 256 48zm-41.6 301.6V162.4L339.2 256l-124.8 93.6z"/></svg>
\ No newline at end of file
diff --git a/ide/qml/Cell.qml b/ide/qml/Cell.qml
new file mode 100644
index 0000000..d060dee
--- /dev/null
+++ b/ide/qml/Cell.qml
@@ -0,0 +1,44 @@
+import QtQuick 2.0
+import QtQuick.Controls 2.15
+import QtQuick.Controls.Material 2.0
+import QtQuick.Layouts 1.0
+
+ColumnLayout {
+ id: root
+
+ RowLayout {
+ Layout.fillWidth: true
+
+ Button {
+ Layout.alignment: Qt.AlignTop
+ icon.source: "qrc:///icons/play-circle.svg"
+ icon.color: Material.color(Material.Grey, Material.Shade600)
+ flat: true
+ }
+
+ ColumnLayout {
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+
+ TextArea {
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ id: code
+ font.family: "monospace"
+ text: "Hello"
+ selectByMouse: true
+ wrapMode: TextEdit.WrapAtWordBoundaryOrAnywhere
+ }
+
+ Label {
+ Layout.fillWidth: true
+ font.family: "monospace"
+ text: "Result\nasdfasdf\nasdad"
+ }
+ }
+ }
+
+ InsertRow {
+
+ }
+}
diff --git a/ide/qml/InsertRow.qml b/ide/qml/InsertRow.qml
new file mode 100644
index 0000000..b12896a
--- /dev/null
+++ b/ide/qml/InsertRow.qml
@@ -0,0 +1,62 @@
+import QtQuick 2.0
+import QtQuick.Controls 2.15
+import QtQuick.Controls.Material 2.0
+import QtQuick.Layouts 1.0
+
+Rectangle {
+ id: root
+
+ color: Material.color(Material.Grey, Material.Shade800)
+ height: 2
+ Layout.fillWidth: true
+ Layout.topMargin: 14
+ Layout.bottomMargin: 14
+
+ transitions: Transition {
+ NumberAnimation {
+ property: "opacity"
+ duration: 100
+ easing.type: Easing.OutCubic
+ }
+ }
+
+ MouseArea {
+ id: mouseArea
+ height: 30
+ width: parent.width
+ anchors.centerIn: parent
+ hoverEnabled: true
+
+ states: [
+ State {
+ when: mouseArea.containsMouse
+ PropertyChanges {
+ target: root
+ opacity: 1
+ }
+ },
+ State {
+ when: !mouseArea.containsMouse
+ PropertyChanges {
+ target: root
+ opacity: 0
+ }
+ }
+ ]
+ }
+
+ Item {
+ id: insertRow
+ anchors.centerIn: parent
+ height: 24
+ width: 24
+
+ Button {
+ id: addButton
+ anchors.centerIn: parent
+ icon.source: "qrc:///icons/add.svg"
+ icon.color: Material.color(Material.Grey, Material.Shade400)
+ flat: true
+ }
+ }
+}
diff --git a/ide/qml/main.qml b/ide/qml/main.qml
new file mode 100644
index 0000000..d3bca25
--- /dev/null
+++ b/ide/qml/main.qml
@@ -0,0 +1,67 @@
+import QtQuick 2.5
+import QtQuick.Controls 2.15
+import QtQuick.Controls.Material 2.0
+import QtQuick.Layouts 1.3
+
+ApplicationWindow {
+ id: root
+ width: 1080
+ height: 720
+ title: "Notebook"
+ visible: true
+
+ Material.theme: Material.Dark
+ Material.accent: Material.Orange
+
+ ColumnLayout {
+ id: column
+ anchors.fill: parent
+
+ TabBar {
+ id: bar
+
+ Layout.fillWidth: true
+
+ TabButton {
+ text: "Example Workspace"
+ }
+
+ TabButton {
+ text: "Another Workspace"
+ }
+
+ TabButton {
+ text: "Testing"
+ }
+ }
+
+ SplitView {
+ id: split
+ Layout.fillHeight: true
+ Layout.fillWidth: true
+ orientation: Qt.Horizontal
+
+ ListView {
+ id: codeEditor
+ SplitView.fillWidth: true
+ SplitView.minimumWidth: 400
+ model: 3
+ clip: true
+
+ delegate: Cell {
+ width: codeEditor.width - 5
+ }
+ }
+
+ Item {
+ id: variables
+ SplitView.minimumWidth: 240
+
+ Label {
+ anchors.centerIn: parent
+ text: "Vars"
+ }
+ }
+ }
+ }
+}
diff --git a/ide/resources.qrc b/ide/resources.qrc
new file mode 100644
index 0000000..bab3643
--- /dev/null
+++ b/ide/resources.qrc
@@ -0,0 +1,9 @@
+<RCC>
+ <qresource prefix="/">
+ <file>qml/main.qml</file>
+ <file>qml/Cell.qml</file>
+ <file>icons/play-circle.svg</file>
+ <file>icons/add.svg</file>
+ <file>qml/InsertRow.qml</file>
+ </qresource>
+</RCC>
diff --git a/main.cpp b/main.cpp
index 7f1a3ad..32f8e6f 100644
--- a/main.cpp
+++ b/main.cpp
@@ -11,6 +11,12 @@
#include "Repl.h"
#include "PPrint.h"
+#ifdef NO_IDE
+using Application = QCoreApplication;
+#else
+#include "ide/IdeMain.h"
+#endif
+
int g_numFailed = 0;
@@ -261,9 +267,9 @@
int main(int argc, char *argv[])
{
- QCoreApplication a(argc, argv);
- QCoreApplication::setApplicationName("REFAL");
- QCoreApplication::setApplicationVersion("1.0-a1");
+ Application a(argc, argv);
+ a.setApplicationName("REFAL");
+ a.setApplicationVersion("1.0-a1");
QCommandLineParser cli;
cli.setApplicationDescription("REFAL interpreter");
@@ -274,6 +280,8 @@
QCommandLineOption testOption(QStringList{"t", "test"}, "Run test suite.");
cli.addOption(testOption);
+ QCommandLineOption replOption(QStringList({"r", "repl"}), "Start CLI REPL");
+ cli.addOption(replOption);
cli.process(a);
@@ -295,9 +303,13 @@
return testResults();
}
- else
+ else if (cli.isSet(replOption))
{
Repl repl;
repl.start();
}
+ else
+ {
+ return ideMain(&a);
+ }
}