blinking cursor
[knntp.git] / egui.d
blobde450941c3d45b5c864355240a0c0798648dce01
1 /* DigitalMars NNTP reader
2 * coded by Ketmar // Invisible Vector <ketmar@ketmar.no-ip.org>
3 * Understanding is not required. Only obedience.
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 module egui is aliced;
19 private:
21 import core.time;
23 import arsd.simpledisplay;
25 import egfx;
27 import iv.cmdcon;
28 import iv.utfutil;
31 // ////////////////////////////////////////////////////////////////////////// //
32 public __gshared SimpleWindow vbwin; // main window
33 public __gshared bool vbfocused = false;
34 public __gshared uint zxtexid; // OpenGL texture id
35 public __gshared int guiGroupListWidth = 110;
36 public __gshared int guiThreadListHeight = 210;
37 public __gshared int lastWinWidth, lastWinHeight;
38 public __gshared bool vbwinLocked = false;
39 public __gshared bool vbufVSync = false;
40 public __gshared bool vbufEffVSync = false;
43 // ////////////////////////////////////////////////////////////////////////// //
44 public class HideMouseEvent {}
45 public class ScreenUpdateEvent {}
46 public class ScreenRepaintEvent {}
47 public class DoConsoleCommandsEvent {}
48 public class QuitEvent {}
49 public class CursorBlinkEvent {}
51 __gshared ScreenUpdateEvent evScrReady;
52 __gshared ScreenRepaintEvent evScreenRepaint;
53 __gshared HideMouseEvent evHideMouse;
54 __gshared DoConsoleCommandsEvent evDoConCommands;
55 __gshared CursorBlinkEvent evCurBlink;
58 shared static this () {
59 evScrReady = new ScreenUpdateEvent();
60 evScreenRepaint = new ScreenRepaintEvent();
61 evHideMouse = new HideMouseEvent();
62 evDoConCommands = new DoConsoleCommandsEvent();
63 evCurBlink = new CursorBlinkEvent();
67 // ////////////////////////////////////////////////////////////////////////// //
68 public void postScreenUpdate () {
69 if (vbwin !is null && !vbwin.eventQueued!ScreenUpdateEvent) vbwin.postEvent(evScrReady);
72 public void postScreenRepaint () {
73 if (vbwin !is null && !vbwin.eventQueued!ScreenRepaintEvent && !vbwin.eventQueued!ScreenUpdateEvent) vbwin.postEvent(evScreenRepaint);
76 public void postDoConCommands () {
77 if (vbwin !is null && !vbwin.eventQueued!DoConsoleCommandsEvent) vbwin.postEvent(evDoConCommands);
80 public void postCurBlink () {
81 if (vbwin !is null && !vbwin.eventQueued!CursorBlinkEvent) {
82 //conwriteln("curblink posted!");
83 vbwin.postTimeout(evCurBlink, 100);
88 // ////////////////////////////////////////////////////////////////////////// //
89 __gshared MonoTime lastMouseMove;
90 enum MouseHideTime = 1000;
93 public bool isMouseVisible () {
94 auto ctt = MonoTime.currTime;
95 return ((ctt-lastMouseMove).total!"msecs" < MouseHideTime);
99 // returns `false` if there is no need to repost the event, and the mouse should be hidden
100 public bool repostHideMouse () {
101 if (vbwin is null || vbwin.eventQueued!HideMouseEvent) return false;
102 auto ctt = MonoTime.currTime;
103 auto tms = (ctt-lastMouseMove).total!"msecs";
104 if (tms >= MouseHideTime) return false;
105 vbwin.postTimeout(evHideMouse, MouseHideTime);
106 return true;
110 public void mouseMoved () {
111 lastMouseMove = MonoTime.currTime;
112 if (vbwin !is null && !vbwin.eventQueued!HideMouseEvent) vbwin.postTimeout(evHideMouse, MouseHideTime);
116 // ////////////////////////////////////////////////////////////////////////// //
117 public void drawTextCursor (int x, int y, int hgt=8) nothrow @trusted @nogc {
118 if (hgt < 1) return;
119 auto ctt = (MonoTime.currTime.ticks*1000/MonoTime.ticksPerSecond)/100;
120 int doty = ctt%(hgt*2-1);
121 if (doty >= hgt) doty = hgt*2-doty-1;
122 gxVLine(x, y, hgt, (ctt%10 < 5 ? gxRGB!(255, 255, 255) : gxRGB!(200, 200, 200)));
123 gxPutPixel(x, y+doty, gxRGB!(0, 255, 255));
127 // ////////////////////////////////////////////////////////////////////////// //
128 public __gshared SubWindow[] subwins;
129 public __gshared bool ignoreSubWinChar = false;
130 public __gshared bool subwinDrag = false;
131 public __gshared int subwinDragXSpot, subwinDragYSpot;
134 public abstract class SubWindow {
135 public:
136 bool immuneToLock = false;
137 int winx, winy, winw, winh;
138 bool closed = false;
140 public:
141 this (int ax, int ay, int aw, int ah) {
142 winx = ax;
143 winy = ay;
144 winw = aw;
145 winh = ah;
148 this (int aw, int ah) {
149 winx = (VBufWidth-aw)/2;
150 winy = (VBufHeight-ah)/2;
151 winw = aw;
152 winh = ah;
155 void onPaint ();
156 void onKey (KeyEvent event);
157 void onMouse (MouseEvent event);
158 void onChar (dchar ch);
160 final void setupClip () {
161 clipReset();
162 clipX0 = winx;
163 clipX1 = winx+winw-1;
164 clipY0 = winy;
165 clipY1 = winy+winh-1;
168 final void setupClientClip () {
169 setupClip();
170 clipX0 += 2;
171 clipX1 -= 2;
172 clipY0 += 12;
173 clipY1 -= 2;
176 void close () {
177 if (closed) return;
178 closed = true;
179 if (subwins.length && subwins[$-1] is this) subwinDrag = false;
180 import std.algorithm : remove;
181 foreach (immutable idx, SubWindow w; subwins) {
182 if (w is this) {
183 subwins = subwins.remove(idx);
184 if (!vbwin.eventQueued!(typeof(evScrReady))) vbwin.postEvent(evScrReady);
185 return;
190 void add (bool fromKeyboard=true) {
191 if (subwins.length && subwins[$-1] is this) return;
192 subwinDrag = false;
193 foreach (immutable idx, SubWindow w; subwins) {
194 if (w is this) {
195 if (idx != subwins.length-1) {
196 import std.algorithm : remove;
197 subwins = subwins.remove(idx);
198 subwins ~= this;
200 if (fromKeyboard) ignoreSubWinChar = true;
201 if (!vbwin.eventQueued!(typeof(evScrReady))) vbwin.postEvent(evScrReady);
202 return;
205 subwins ~= this;
206 if (fromKeyboard) ignoreSubWinChar = true;
207 if (!vbwin.eventQueued!(typeof(evScrReady))) vbwin.postEvent(evScrReady);
212 // ////////////////////////////////////////////////////////////////////////// //
213 // setup cliprect first
214 public final class LineEdit {
215 private:
216 int pos;
217 int lskip;
218 string s;
220 public:
221 bool active;
223 public:
224 this () {}
226 @property string str () const pure nothrow @safe @nogc { return s; }
227 @property void str (string ns) nothrow @safe @nogc { s = ns; pos = s.utflen; }
229 void makeCursorVisible () {
230 int w = clipWidth-2;
231 if (w < 6*3) w = 6*3;
232 int tw = gxTextWidthUtf(s);
233 if (tw < w) { lskip = 0; return; }
234 if (lskip > tw-w) lskip = tw-w;
235 // left
236 int px = gxTextWidthUtf(s.utfleft(pos));
237 if (px < lskip) lskip = px-6;
238 if (lskip < 0) lskip = 0;
239 // right
240 int vx = px-lskip;
241 if (px >= w) lskip = px-(w-6);
242 if (lskip < 0) lskip = 0;
245 void onPaint () {
246 makeCursorVisible();
247 gxFillRect(clipX0, clipY0, clipWidth, 10, gxRGB!(0, 0, 0));
248 clipX0 += 2;
249 clipX1 -= 2;
250 gxDrawTextUtf(clipX0-lskip, clipY0+1, s, (active ? gxRGB!(220, 220, 80) : gxRGB!(220, 220, 0)));
251 if (active) {
252 drawTextCursor(clipX0-lskip+gxTextWidthUtf(s.utfleft(pos)), clipY0+1);
253 postCurBlink();
255 if (lskip > 0) gxDrawTextOutP(clipX0+1, clipY0+1, "\x1b", gxRGB!(0, 220, 220), gxRGB!(0, 0, 0));
256 int pxlen = gxTextWidthUtf(s);
257 if (clipX0-lskip+pxlen-1 > clipX1) gxDrawTextOutP(clipX1-6, clipY0+1, "\x1a", gxRGB!(0, 220, 220), gxRGB!(0, 0, 0));
260 void onKey (KeyEvent event) {
261 if (!event.pressed) return;
262 if (event == "Left" || event == "Pad4") { if (pos > 0) --pos; return; }
263 if (event == "Right" || event == "Pad6") { if (pos < s.utflen) ++pos; return; }
264 if (event == "Home" || event == "Pad7") { pos = 0; return; }
265 if (event == "End" || event == "Pad1") { pos = s.utflen; return; }
266 if (event == "Delete" || event == "PadDot") { if (pos < s.utflen) s = s.utfdel(pos, 1); return; }
267 if (event == "S-Insert") {
268 getClipboardText(vbwin, delegate (in char[] text) {
269 Utf8DecoderFast dc;
270 foreach (char ch; text) {
271 if (dc.decode(cast(ubyte)ch)) {
272 dchar dch = dc.codepoint;
273 if (dc.isValidDC(dch) && dch != 8) onChar(dch);
280 void onChar (dchar ch) {
281 if (ch == 8) {
282 if (pos > 0) {
283 --pos;
284 s = s.utfdel(pos, 1);
286 return;
288 if (ch < ' ' || ch == 127) return;
289 string t = s.utfleft(pos);
290 t ~= ch;
291 t ~= s.utfskip(pos);
292 if (t.length > 255) return;
293 s = t;
294 ++pos;