1 module editor; 2 3 import pngtext.pngtext; 4 5 import utils.misc; 6 7 import qui.qui; 8 import qui.widgets; 9 10 import std.stdio; 11 12 /// The in-terminal text editor using qui.widgets.MemoWidget 13 class Editor{ 14 private: 15 /// the terminal 16 QTerminal _terminal; 17 /// the text editor 18 MemoWidget _editor; 19 /// to occupy space on left of 20 SplitterWidget _statusBarLeft; 21 /// shows the shortcut keys, sits next to _statusLabel 22 TextLabelWidget _shortcutLabel; 23 /// Contains the and _shortcutLabel 24 QLayout _statusBar; 25 /// filename of original png 26 string _inputPng; 27 /// filename of output png 28 string _outputPng; 29 public: 30 /// constructor 31 /// 32 /// if text only has to be displayed, use readOnly=true, and no need to specify saveAs, just leave it blank 33 this(string image, string saveAs, bool readOnly=false){ 34 _inputPng = image; 35 _outputPng = saveAs; 36 // setup the terminal 37 _terminal = new QTerminal(QLayout.Type.Vertical); 38 _statusBar = new QLayout(QLayout.Type.Horizontal); 39 _editor = new MemoWidget(!readOnly); 40 _statusBarLeft = new SplitterWidget(); 41 _shortcutLabel = new TextLabelWidget(); 42 // set up each widget 43 // first comes the editor: 44 _editor.wantsTab = false; 45 _editor.lines.loadArray(separateLines(cast(char[])readDataFromPng(_inputPng)));//load the lines 46 // now comes the _statusLabel 47 // now the _shortcutLabel 48 _shortcutLabel.textColor = DEFAULT_BG; 49 _shortcutLabel.backgroundColor = DEFAULT_FG; 50 _shortcutLabel.caption = "Ctrl+C - Save & Exit"; 51 _shortcutLabel.size.maxWidth = _shortcutLabel.caption.length; 52 // put both of these in _statusBar, and set it up too 53 _statusBar.addWidget([_statusBarLeft, _shortcutLabel]); 54 _statusBar.size.maxHeight = 1; 55 _statusBar.size.minHeight = 1; 56 // put all those in QTerminal 57 _terminal.addWidget([_editor, _statusBar]); 58 // register all widgets 59 _terminal.registerWidget([_editor, _statusBar, _statusBarLeft, _shortcutLabel]); 60 // and its done, ready to start*/ 61 } 62 /// destructor 63 ~this(){ 64 .destroy(_terminal); 65 .destroy(_editor); 66 .destroy(_statusBar); 67 .destroy(_statusBarLeft); 68 .destroy(_shortcutLabel); 69 } 70 /// runs the editor 71 /// 72 /// Returns: false if there were errors(s) 73 bool run(){ 74 _terminal.run; 75 // save 76 const string[] lines = _editor.lines.toArray; 77 ubyte[] data; 78 uinteger writeTo = 0; 79 foreach (line; lines){ 80 data.length += line.length+1; 81 foreach (ch; line){ 82 data[writeTo] = cast(ubyte)ch; 83 writeTo++; 84 } 85 data[writeTo] = '\n'; 86 writeTo++; 87 } 88 const string[] errors = writeDataToPng(_inputPng, _outputPng, data); 89 if (errors.length){ 90 stderr.writeln("Errors while writing to image:"); 91 foreach (err; errors){ 92 stderr.writeln(err); 93 } 94 return false; 95 } 96 return true; 97 } 98 99 } 100 101 /// reads a single string into string[], separating the lines 102 private string[] separateLines(string s){ 103 string[] r; 104 for(uinteger i = 0, readFrom = 0; i < s.length; i ++){ 105 if (s[i] == '\n'){ 106 r ~= s[readFrom .. i].dup; 107 readFrom = i+1; 108 }else if (i+1 == s.length){ 109 r ~= s[readFrom .. i + 1].dup; 110 } 111 } 112 return r; 113 } 114 /// ditto 115 private string[] separateLines(char[] s){ 116 return separateLines(cast(string)s); 117 }