1import QtQuick 2.7
2import QtQuick.Controls 1.4
3import QtQuick.Controls.Styles 1.4
4import QtQuick.Layouts 1.2
5import QtQuick.Window 2.1
6import QtQuick.Dialogs 1.2
7import radiance 1.0
8import "."
9
10ApplicationWindow {
11    id: window
12    title: "GLSL Editor"
13    property string file
14    property var afterSave
15    property int line
16    property int col
17    signal saved()
18
19    menuBar: MenuBar {
20        Menu {
21            title: "&File"
22            MenuItem {
23                action: newAction
24            }
25            MenuItem {
26                action: openAction
27            }
28            MenuItem {
29                action: saveAction
30            }
31            MenuItem {
32                action: saveAsAction
33            }
34            MenuSeparator { }
35            MenuItem {
36                action: revertAction
37            }
38            MenuSeparator { }
39            MenuItem {
40                action: closeAction
41            }
42        }
43    }
44
45    statusBar: StatusBar {
46        RowLayout {
47            Label { text: glslDocument.message }
48        }
49    }
50
51    Action {
52        id: newAction
53        text: "&New..."
54        shortcut: "Ctrl+N"
55        onTriggered: {
56            tryClear();
57        }
58    }
59
60    Action {
61        id: openAction
62        text: "&Open..."
63        shortcut: "Ctrl+O"
64        onTriggered: {
65            showOpenDialog();
66        }
67    }
68
69    Action {
70        id: saveAction
71        text: "&Save"
72        shortcut: "Ctrl+S"
73        onTriggered: {
74            if (window.file) {
75                save();
76            } else {
77                showSaveDialog();
78            }
79        }
80    }
81
82    Action {
83        id: saveAsAction
84        text: "Save &As..."
85        onTriggered: {
86            showSaveDialog();
87        }
88    }
89
90    Action {
91        id: revertAction
92        text: "Revert"
93        shortcut: "Ctrl+R"
94        onTriggered: {
95            revert();
96        }
97    }
98
99    Action {
100        id: closeAction
101        text: "E&xit"
102        shortcut: "Ctrl+Q"
103        onTriggered: {
104            tryClose()
105        }
106    }
107
108    onClosing: {
109        closeAction.trigger();
110        close.accepted = false;
111    }
112
113    function urlToPath(url) {
114        var path = url.toString();
115        // remove prefixed "file://"
116        path = path.replace(/^(file:\/{2})/,"");
117        return path;
118    }
119
120    function showOpenDialog() {
121        openDialog.folder = glslDocument.loadDirectory(window.file);
122        openDialog.open();
123    }
124
125    function showSaveDialog() {
126        saveAsDialog.folder = glslDocument.saveDirectory(window.file);
127        saveAsDialog.open();
128    }
129
130    FileDialog {
131        id: saveAsDialog
132        title: "Save As"
133        selectExisting: false
134        nameFilters: [ "GLSL files (*.glsl)", "All files (*)" ]
135        onAccepted: {
136            var path = urlToPath(saveAsDialog.fileUrl);
137            if (path) {
138                window.file = glslDocument.contractLibraryPath(path);
139                save();
140            }
141            afterSave = null;
142        }
143        onRejected: {
144            afterSave = null;
145        }
146    }
147
148    FileDialog {
149        id: openDialog
150        title: "Open"
151        nameFilters: [ "GLSL files (*.glsl)", "All files (*)" ]
152        onAccepted: {
153            var path = urlToPath(openDialog.fileUrl);
154            if (path) {
155                window.file = glslDocument.contractLibraryPath(path);
156                load();
157            }
158        }
159        onRejected: {
160        }
161    }
162
163    MessageDialog {
164        id: saveChangesBeforeClose
165        title: "Save changes?"
166        icon: StandardIcon.Question
167        text: window.file ? "Save changes to \"" + window.file + "\" before closing?" : "Save changes before closing?"
168        standardButtons: StandardButton.Discard | StandardButton.Cancel | StandardButton.Save
169        onDiscard: {
170            window.close();
171        }
172        onAccepted: {
173            afterSave = window.close;
174            saveAction.trigger();
175        }
176    }
177
178    MessageDialog {
179        id: saveChangesBeforeClear
180        title: "Save changes?"
181        icon: StandardIcon.Question
182        text: window.file ? "Save changes to \"" + window.file + "\" before clearing?" : "Save changes before clearing?"
183        standardButtons: StandardButton.Discard | StandardButton.Cancel | StandardButton.Save
184        onDiscard: {
185            window.clear();
186        }
187        onAccepted: {
188            afterSave = window.clear;
189            saveAction.trigger();
190        }
191    }
192
193    TextArea {
194        id: textArea
195        anchors.top: parent.top
196        anchors.bottom: parent.bottom
197        anchors.left: parent.left
198        anchors.right: parent.right
199        textFormat: Qt.RichText
200        Component.onCompleted: forceActiveFocus()
201        font.family: "Monospace"
202        style: TextAreaStyle {
203            textColor: RadianceStyle.editorTextColor
204            backgroundColor: RadianceStyle.editorBackgroundColor
205        }
206        frameVisible: false
207        wrapMode: TextEdit.NoWrap
208
209        Keys.onPressed: {
210            if (event.key == Qt.Key_Tab) {
211                textArea.remove(textArea.selectionStart, textArea.selectionEnd);
212                textArea.insert(textArea.cursorPosition, "    ");
213                event.accepted = true;
214            }
215        }
216    }
217
218    GlslDocument {
219        id: glslDocument
220        document: textArea.textDocument
221    }
222
223    GlslHighlighter {
224        document: textArea.textDocument
225    }
226
227    function load() {
228        if (glslDocument.load(window.file)) {
229            setCursor();
230        }
231    }
232
233    function setCursor() {
234        var pos = glslDocument.cursorPositionAt(line, col);
235        textArea.cursorPosition = pos;
236    }
237
238    function save() {
239        if (glslDocument.save(window.file)) {
240            window.saved();
241            if (afterSave) {
242                afterSave();
243            }
244        }
245        afterSave = null;
246    }
247
248    function close() {
249        window.visible = false;
250    }
251
252    function clear() {
253        window.file = ""
254        glslDocument.clear();
255    }
256
257    function open(file) {
258        if (window.visible && file == window.file) {
259            setCursor();
260        } else {
261            window.file = file;
262            if (window.file) {
263                load();
264            }
265            window.visible = true;
266        }
267        window.afterSave = null;
268    }
269
270    function tryClear() {
271        if (glslDocument.modified) {
272            saveChangesBeforeClear.open();
273        } else {
274            clear();
275        }
276    }
277
278    function tryClose() {
279        if (glslDocument.modified) {
280            saveChangesBeforeClose.open();
281        } else {
282            close();
283        }
284    }
285
286    function revert() {
287        glslDocument.revert(window.file);
288        saved(); // Not actually but effectively
289    }
290}
291