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