fixed toxcore deprecations
[bioacid.git] / miniedit.d
blobc57416aea8e301050a07fee21ce8774b79784be0
1 /* coded by Ketmar // Invisible Vector <ketmar@ketmar.no-ip.org>
2 * Understanding is not required. Only obedience.
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 module miniedit is aliced;
18 private:
19 import arsd.color;
20 import arsd.simpledisplay;
22 import iv.cmdcon;
23 import iv.cmdcon.gl;
24 import iv.nanovega;
25 import iv.strex;
26 import iv.sdpyutil;
27 import iv.unarray;
28 import iv.utfutil;
29 import iv.vfs.io;
32 // ////////////////////////////////////////////////////////////////////////// //
33 public final class MiniEdit {
34 private import iv.utfutil : Utf8Decoder;
35 private:
36 dchar[] dtext;
37 NVGGlyphPosition[8192] glyphs;
38 Utf8Decoder ec; // encoder for clipboard gets
39 char[8] clipbuf;
40 uint clipbufused;
42 private:
43 void putChar (char ch) {
44 //if (ch <= ' ' || ch >= 127) conprintf("new char: 0x%02x\n", cast(ubyte)ch); else conprintf("char: '%s'\n", ch);
45 dchar dc = ec.decode(cast(ubyte)ch);
46 if (dc <= dchar.max) {
47 // decoded
48 if (dc >= ' ' || dc == '\n') {
49 dtext ~= dc;
50 glconPostScreenRepaint();
55 public:
56 this () {}
58 string text () const nothrow {
59 import iv.utfutil : utf8Encode;
60 string res;
61 res.reserve(dtext.length*4);
62 foreach (dchar dc; dtext) {
63 char[4] buf = void;
64 auto len = utf8Encode(buf[], dc);
65 res ~= buf[0..len];
67 return res;
70 void clear () {
71 if (dtext.length) {
72 dtext.length = 0;
73 dtext.assumeSafeAppend;
77 void setFont (NVGContext nvg) {
78 nvg.fontSize = 20;
79 nvg.fontFace = "ui";
80 nvg.textAlign(NVGTextAlign.H.Left, NVGTextAlign.V.Top);
83 static struct HeightInfo {
84 float height = 0;
85 int lines = 0;
86 float lineh = 0;
88 void newline () nothrow @safe @nogc { ++lines; height += lineh; }
91 // return `false` from delegate to stop
92 // `line` in delegate cannot be empty
93 // if `line` ends with '\n', this is hard newline, otherwise it is a wrap
94 void byLine (NVGContext nvg, int width, scope bool delegate (const(dchar)[] line) dg) {
95 assert(nvg !is null);
96 assert(dg !is null);
98 setFont(nvg);
99 if (width < 1) width = 1; // just for fun
101 uint curpos = 0;
102 while (curpos < dtext.length) {
103 // new line; always here
104 uint epos = 0;
105 nvg.textGlyphPositions(0, 0, dtext[curpos..$], delegate (NVGGlyphPosition gpos) {
106 if (gpos.maxx >= width) return false; // doesn't fit: stop
107 if (dtext[curpos+gpos.strpos] == '\n') return false; // eol hit: stop
108 epos = cast(uint)gpos.strpos+1; // swallow the glyph
109 return true; // continue
111 // no glyphs fit, but the line is not empty: swallow at least one glyph
112 if (epos == 0 && dtext[curpos] != '\n') ++epos;
113 // if we hit (or about to hit) EOL, swallow it too
114 if (epos == 0) { assert(dtext[curpos] == '\n'); ++epos; }
115 else if (curpos+epos < dtext.length && dtext[curpos+epos] == '\n') ++epos;
116 if (dg(dtext[curpos..curpos+epos])) break;
117 curpos += epos;
121 // for the given width
122 HeightInfo calcHeight (NVGContext nvg, int width) {
123 setFont(nvg);
125 HeightInfo res;
126 nvg.textMetrics(null, null, &res.lineh);
128 byLine(nvg, width, delegate (line) {
129 res.newline();
130 return false;
133 if (dtext.length && dtext[$-1] == '\n') res.newline();
135 // we always has at least one line
136 if (res.lines == 0) res.newline();
138 return res;
141 void draw (NVGContext nvg, float x, float y, int width, int height) {
142 if (height < 1) return;
144 setFont(nvg);
146 float lineh;
147 nvg.textMetrics(null, null, &lineh);
149 //nvg.beginPath();
150 nvg.fillColor = NVGColor.k8orange;
151 //nvg.text(x, y, text[0..repos]);
153 float ex = x;
154 float ey = y;
155 byLine(nvg, width, delegate (line) {
156 if (line[$-1] == '\n') {
157 // hard wrap
158 ex = x;
159 ey = y+lineh;
160 nvg.text(x, y, line[0..$-1]);
161 } else {
162 // soft wrap
163 ex = nvg.text(x, y, line);
164 ey = y;
166 y += lineh;
167 return false;
170 nvg.beginPath();
171 nvg.strokeColor = NVGColor.yellow;
172 nvg.rect(cast(int)ex, cast(int)ey, 1, height); // ensure that cursor looks a little blurry
173 nvg.stroke();
176 bool onKey (KeyEvent event) {
177 // enter
178 if (event.key == Key.Enter) {
179 if (event.pressed) {
180 dtext ~= '\n';
181 glconPostScreenRepaint();
183 return true;
186 // bs
187 if (event.key == Key.Backspace) {
188 if (event.pressed && dtext.length > 0) {
189 dtext.length -= 1;
190 dtext.assumeSafeAppend;
191 glconPostScreenRepaint();
193 return true;
196 // C-Y
197 if (event == "D-C-Y" || event == "U-C-Y") {
198 if (event.pressed && dtext.length > 0) {
199 clear();
200 glconPostScreenRepaint();
202 return true;
205 if (event == "D-S-Insert" || event == "U-S-Insert") {
206 if (event.pressed) glconCtlWindow.getClipboardText(delegate (str) { foreach (immutable char ch; str) putChar(ch); });
207 return true;
210 if (event == "D-C-Insert" || event == "U-C-Insert") {
211 if (event.pressed) glconCtlWindow.setClipboardText(text);
212 return true;
215 return false;
218 bool onChar (dchar ch) {
219 if (ch >= ' ') {
220 dtext ~= ch;
221 glconPostScreenRepaint();
222 return true;
225 return false;