(some) keyboard control in playlist
[amper.git] / egfx / backx.d
blob43b710bd74ad492482cc146c5c4fba8104b555dc
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 egfx.backx;
18 private:
20 import arsd.simpledisplay;
22 import iv.cmdcon;
23 import iv.cmdcongl;
25 import egfx.base;
27 version = egfx_more_backbuffers;
30 // ////////////////////////////////////////////////////////////////////////// //
31 public class EWindow {
32 protected:
33 int mWidth, mHeight;
34 bool mActive;
35 EWidget[] widgets;
36 int mActiveWidget = -1;
38 public:
39 this (int awidth, int aheight) {
40 if (awidth < 1) awidth = 1;
41 if (aheight < 1) aheight = 1;
42 mWidth = awidth;
43 mHeight = aheight;
46 final @property int width () const pure nothrow @safe @nogc { pragma(inline, true); return mWidth; }
47 final @property int height () const pure nothrow @safe @nogc { pragma(inline, true); return mHeight; }
49 void setSize (int awdt, int ahgt) {
50 if (awdt < 1) awdt = 1;
51 if (ahgt < 1) ahgt = 1;
52 mWidth = awdt;
53 mHeight = ahgt;
56 EWidget addWidget (EWidget w) {
57 if (w is null) return null;
58 if (w.parent !is null) throw new Exception("widget already owned");
59 widgets ~= w;
60 w.parent = this;
61 return w;
64 void defocused () {
65 if (mActive) {
66 activeWidget = null;
67 mActive = false;
68 glconPostScreenRebuild();
72 void focused () {
73 if (!active) {
74 mActive = true;
75 glconPostScreenRebuild();
79 final void focusChanged (bool focused) { if (focused) this.focused(); else this.defocused(); }
81 final @property bool active () const pure nothrow @safe @nogc { pragma(inline, true); return mActive; }
82 final @property void active (bool v) { if (mActive == v) return; if (v) focused(); else defocused(); }
84 final @property EWidget activeWidget () pure nothrow @safe @nogc { pragma(inline, true); return (mActiveWidget >= 0 && mActiveWidget < widgets.length ? widgets[mActiveWidget] : null); }
86 final @property void activeWidget (EWidget w) {
87 EWidget oaw = (mActiveWidget >= 0 && mActiveWidget < widgets.length ? widgets[mActiveWidget] : null);
88 if (w is null || w.parent !is this) {
89 mActiveWidget = -1;
90 if (oaw !is null) oaw.onDeactivate();
91 return;
93 foreach (immutable idx, EWidget ww; widgets) {
94 if (ww is w) {
95 if (mActiveWidget == idx) return;
96 mActiveWidget = cast(int)idx;
97 if (oaw !is null) oaw.onDeactivate();
98 ww.onActivate();
99 return;
102 mActiveWidget = -1;
103 if (oaw !is null) oaw.onDeactivate();
106 final EWidget widgetAt (int x, int y) pure nothrow @safe @nogc {
107 foreach_reverse (EWidget w; widgets) {
108 if (w.rc.inside(x, y)) return w;
110 return null;
113 protected void paintWidgets () {
114 foreach (EWidget w; widgets) {
115 gxClipRect = w.rc;
116 w.onPaint();
120 protected void paintBackground () {}
121 protected void paintFinished () {}
123 void onPaint () {
124 gxClipReset();
125 paintBackground();
126 paintWidgets();
127 gxClipReset();
128 paintFinished();
131 bool onKeyPre (KeyEvent event) { return false; }
132 bool onKeyPost (KeyEvent event) { return false; }
134 bool onKey (KeyEvent event) {
135 if (onKeyPre(event)) return true;
136 if (auto aw = activeWidget) {
137 if (aw.onKey(event)) return true;
139 if (onKeyPost(event)) return true;
140 return false;
143 bool onCharPre (dchar ch) { return false; }
144 bool onCharPost (dchar ch) { return false; }
146 bool onChar (dchar ch) {
147 if (onCharPre(ch)) return true;
148 if (auto aw = activeWidget) {
149 if (aw.onChar(ch)) return true;
151 if (onCharPost(ch)) return true;
152 return false;
155 bool onMousePre (MouseEvent event) { return false; }
156 bool onMousePost (MouseEvent event) { return false; }
158 bool onMouse (MouseEvent event) {
159 bool wasAwOrAct = false;
160 if (onMousePre(event)) return true;
161 auto aw = activeWidget;
162 if (aw !is null) {
163 if (aw.onMouse(event)) return true;
164 wasAwOrAct = true;
166 if (auto ww = widgetAt(event.x, event.y)) {
167 if (ww !is aw) {
168 if (ww.onMouse(event)) return true;
169 wasAwOrAct = true;
172 if (onMousePost(event)) return true;
173 if (wasAwOrAct && event.type != MouseEventType.motion) return true;
174 return false;
179 // ////////////////////////////////////////////////////////////////////////// //
180 public class EWidget {
181 EWindow parent;
182 GxRect rc;
184 this (GxRect arc) {
185 rc = arc;
188 final @property bool active () nothrow @trusted @nogc { pragma(inline, true); return (parent !is null && parent.activeWidget is this); }
189 final @property void active (bool v) { pragma(inline, true); if (parent !is null) parent.activeWidget = this; }
191 void onPaint () {}
193 void onActivate () {} // parent.activeWidget is this
194 void onDeactivate () {} // parent.activeWidget already changed
196 bool onKey (KeyEvent event) { return false; }
197 bool onChar (dchar ch) { return false; }
198 bool onMouse (MouseEvent event) { return false; }
202 // ////////////////////////////////////////////////////////////////////////// //
203 shared static this () {
204 import core.stdc.stdlib : malloc;
205 vglTexBuf = cast(uint*)malloc((VBufWidth*VBufHeight+4)*4);
206 if (vglTexBuf is null) assert(0, "out of memory!");
207 vglTexBuf[0..VBufWidth*VBufHeight] = 0;
211 // ////////////////////////////////////////////////////////////////////////// //
212 // resize buffer, reinitialize OpenGL texture
214 public void egxResizeBuffer (int wdt, int hgt, int ascale=1) {
215 if (wdt < 1) wdt = 1;
216 if (hgt < 1) hgt = 1;
218 if (wdt > 8192) wdt = 8192;
219 if (hgt > 8192) hgt = 8192;
221 bool sizeChanged = (wdt != VBufWidth || hgt != VBufHeight);
222 VBufWidth = wdt;
223 VBufHeight = hgt;
225 if (vglTexBuf is null || sizeChanged) {
226 import core.stdc.stdlib : realloc;
227 vglTexBuf = cast(uint*)realloc(vglTexBuf, (VBufWidth*VBufHeight+4)*vglTexBuf[0].sizeof);
228 if (vglTexBuf is null) assert(0, "out of memory!");
229 vglTexBuf[0..VBufWidth*VBufHeight] = 0;
232 if (ascale < 1) ascale = 1;
233 if (ascale > 32) ascale = 32;
234 vbufEffScale = cast(ubyte)ascale;
239 // ////////////////////////////////////////////////////////////////////////// //
240 __gshared EgfxWindow mainwin;
242 public void egfxSetMainWindow (EgfxWindow win) {
243 if (win !is null) {
244 if (mainwin is win) return;
245 static if (is(typeof(&mainwin.closeQuery))) {
246 if (mainwin !is null) mainwin.closeQuery = null;
248 mainwin = win;
249 static if (is(typeof(&mainwin.closeQuery))) {
250 mainwin.closeQuery = delegate () { concmd("quit"); glconPostDoConCommands(); };
252 glconBackBuffer = win.backbuf;
253 } else {
254 glconBackBuffer = null;
259 // ////////////////////////////////////////////////////////////////////////// //
260 public class EgfxWindow : SimpleWindow {
261 EWindow ampw;
262 Image backbuf;
263 version(egfx_more_backbuffers) uint* egxBackBuf;
265 // minsize will be taken from aampw
266 // if resizestep is zero, size on that dimension is fixed
267 this (EWindow aampw, string winclass, string title, int resizeXStep=0, int resizeYStep=0) {
268 if (aampw is null) assert(0, "wtf?! no EWindow!");
269 if (winclass.length) sdpyWindowClass = winclass;
270 ampw = aampw;
271 int minw = aampw.width;
272 int maxw = aampw.width;
273 int minh = aampw.height;
274 int maxh = aampw.height;
275 super(minw, minh, title, OpenGlOptions.no, Resizability.allowResizing, WindowTypes.undecorated);
276 if (resizeXStep > 0) maxw = 4096;
277 if (resizeYStep > 0) maxh = 4096;
278 setMinSize(minw, minh);
279 setMaxSize(maxw, maxh);
280 if (resizeXStep > 0 || resizeYStep > 0) {
281 if (resizeXStep <= 0) resizeXStep = 1;
282 if (resizeYStep <= 0) resizeYStep = 1;
283 setResizeGranularity(resizeXStep, resizeYStep);
285 ampw = aampw;
286 backbuf = new Image(minw, minh);
287 setupHandlers();
290 private void freeBackBuffer () {
291 version(egfx_more_backbuffers) {
292 if (egxBackBuf !is null) {
293 import core.stdc.stdlib : free;
294 free(egxBackBuf);
295 egxBackBuf = null;
300 version(egfx_more_backbuffers) ~this () { freeBackBuffer(); }
302 override void close () {
303 freeBackBuffer();
304 super.close();
307 void redraw () {
308 if (closed || backbuf is null) return;
310 auto saveVBufWidth = VBufWidth;
311 auto saveVBufHeight = VBufHeight;
312 auto savevglTexBuf = vglTexBuf;
313 scope(exit) {
314 VBufWidth = saveVBufWidth;
315 VBufHeight = saveVBufHeight;
316 vglTexBuf = savevglTexBuf;
318 //conwriteln("backbuf size: (", backbuf.width, "x", backbuf.height, ")");
319 VBufWidth = backbuf.width;
320 VBufHeight = backbuf.height;
321 version(egfx_more_backbuffers) {
322 if (egxBackBuf is null) {
323 import core.stdc.stdlib : realloc;
324 egxBackBuf = cast(uint*)realloc(egxBackBuf, (VBufWidth*VBufHeight+4)*egxBackBuf[0].sizeof);
325 if (egxBackBuf is null) assert(0, "out of memory!");
326 egxBackBuf[0..VBufWidth*VBufHeight] = 0;
328 vglTexBuf = egxBackBuf;
329 } else {
330 vglTexBuf = cast(uint*)backbuf.getDataPointer;
332 gxClipReset();
333 ampw.onPaint();
334 version(egfx_more_backbuffers) {
335 import core.stdc.string;
336 memcpy(backbuf.getDataPointer, vglTexBuf, VBufWidth*VBufHeight*4);
339 if (mainwin is this) glconDraw();
341 auto painter = this.draw();
342 painter.drawImage(Point(0, 0), backbuf);
346 protected void setupHandlers () {
347 handleKeyEvent = delegate (KeyEvent event) {
348 scope(exit) if (!conQueueEmpty()) glconPostDoConCommands();
349 if (mainwin is this && glconKeyEvent(event)) { glconPostScreenRepaint(); return; }
350 if (isQuitRequested) { close(); return; }
351 if ((event.modifierState&ModifierState.numLock) == 0) {
352 switch (event.key) {
353 case Key.Pad0: event.key = Key.Insert; break;
354 case Key.Pad1: event.key = Key.End; break;
355 case Key.Pad2: event.key = Key.Down; break;
356 case Key.Pad3: event.key = Key.PageDown; break;
357 case Key.Pad4: event.key = Key.Left; break;
358 //case Key.Pad5: event.key = Key.Insert; break;
359 case Key.Pad6: event.key = Key.Right; break;
360 case Key.Pad7: event.key = Key.Home; break;
361 case Key.Pad8: event.key = Key.Up; break;
362 case Key.Pad9: event.key = Key.PageUp; break;
363 case Key.PadEnter: event.key = Key.Enter; break;
364 case Key.PadDot: event.key = Key.Delete; break;
365 default: break;
367 } else {
368 if (event.key == Key.PadEnter) event.key = Key.Enter;
370 ampw.onKey(event);
371 glconPostScreenRebuild();
374 handleMouseEvent = delegate (MouseEvent event) {
375 scope(exit) if (!conQueueEmpty()) glconPostDoConCommands();
376 if (isQuitRequested) { close(); return; }
377 //event.x /= vbufEffScale;
378 //event.y /= vbufEffScale;
379 ampw.onMouse(event);
380 glconPostScreenRebuild();
383 handleCharEvent = delegate (dchar ch) {
384 scope(exit) if (!conQueueEmpty()) glconPostDoConCommands();
385 if (mainwin is this && glconCharEvent(ch)) { glconPostScreenRepaint(); return; }
386 if (isQuitRequested) { close(); return; }
387 ampw.onChar(ch);
388 glconPostScreenRebuild();
391 windowResized = delegate (int wdt, int hgt) {
392 scope(exit) if (!conQueueEmpty()) glconPostDoConCommands();
393 if (isQuitRequested) { close(); return; }
394 if (wdt < 1) wdt = 1;
395 if (hgt < 1) hgt = 1;
396 if (mainwin is this) glconResize(wdt, hgt);
397 if (backbuf.width != wdt || backbuf.height != hgt) {
398 //delete backbuf;
399 backbuf = new Image(wdt, hgt);
400 if (mainwin is this) glconBackBuffer = this.backbuf;
401 freeBackBuffer();
403 ampw.mWidth = wdt;
404 ampw.mHeight = hgt;
405 redraw();
406 //glconPostScreenRebuild();
409 onFocusChange = delegate (bool focused) { ampw.active = focused; };