1 module amperskin
is aliced
;
6 import arsd
.simpledisplay
;
16 // ////////////////////////////////////////////////////////////////////////// //
20 void postQuitEvent () {
21 if (glconMainWindow
is null) return;
22 if (!glconMainWindow
.eventQueued
!QuitEvent
) glconMainWindow
.postEvent(new QuitEvent());
26 // ////////////////////////////////////////////////////////////////////////// //
27 //__gshared string skinfile = "WastedAMP1-0.zip";
28 __gshared string skinfile
= "winamp2.zip";
29 __gshared
bool modeShuffle
= false;
30 __gshared
bool modeRepeat
= false;
32 shared static this () {
33 conRegVar
!modeShuffle("mode_shuffle", "shuffle playlist");
34 conRegVar
!modeRepeat("mode_repeat", "repeat playlist");
38 // ////////////////////////////////////////////////////////////////////////// //
39 // width: 275; height: shaded: 14, full: 116
40 __gshared EImage skinMainImg
;
41 __gshared EImage skinMainCButtons
;
42 __gshared EImage skinMainShufRep
;
45 // ////////////////////////////////////////////////////////////////////////// //
46 __gshared AmpWindow ampMain
;
49 // ////////////////////////////////////////////////////////////////////////// //
50 AmpWindow
createAmpMain () {
51 AmpWindow w
= new AmpWindow(skinMainImg
);
52 w
.addWidget(new AmpWidget(skinMainCButtons
, 16, 88, GxRect(0, 0, 23, 18), "song_prev"));
53 w
.addWidget(new AmpWidget(skinMainCButtons
, 39, 88, GxRect(23, 0, 23, 18), "song_play"));
54 w
.addWidget(new AmpWidget(skinMainCButtons
, 62, 88, GxRect(46, 0, 23, 18), "song_pause"));
55 w
.addWidget(new AmpWidget(skinMainCButtons
, 85, 88, GxRect(69, 0, 23, 18), "song_stop"));
56 w
.addWidget(new AmpWidget(skinMainCButtons
, 108, 88, GxRect(92, 0, 22, 18), "song_next"));
57 w
.addWidget(new AmpWidget(skinMainCButtons
, 136, 88, GxRect(114, 0, 22, 16), "song_eject"));
58 w
.addWidget(new AmpWidgetToggle(&modeShuffle
, skinMainShufRep
, 164, 89, GxRect(28, 0, 46, 15), "mode_shuffle toggle"));
59 w
.addWidget(new AmpWidgetToggle(&modeRepeat
, skinMainShufRep
, 210, 89, GxRect(0, 0, 28, 15), "mode_repeat toggle"));
64 // ////////////////////////////////////////////////////////////////////////// //
66 static bool isGoodImageExtension (ConString fname
) {
67 auto epos
= fname
.lastIndexOf('.');
68 if (epos
< 0) return false;
69 fname
= fname
[epos
..$];
71 fname
.strEquCI(".bmp") ||
72 fname
.strEquCI(".png") ||
73 fname
.strEquCI(".jpg") ||
74 fname
.strEquCI(".jpeg") ||
78 void loadImage (EImage
* img
, string fname
) {
79 auto epos
= fname
.lastIndexOf('.');
80 if (epos
< 0) throw new Exception("invalid skin image '"~fname
~"'");
81 auto ext
= fname
[epos
..$];
83 auto fl
= VFile(fname
);
85 if (sz
< 1 || sz
> 1024*1024*8) throw new Exception("invalid skin image '"~fname
~"'");
87 auto data
= new ubyte[](cast(uint)sz
);
88 scope(exit
) delete data
;
89 fl
.rawReadExact(data
);
92 scope(exit
) delete ximg
;
94 if (ext
.strEquCI(".bmp")) ximg
= readBmp(data
);
95 else if (ext
.strEquCI(".png")) ximg
= imageFromPng(readPng(data
));
96 else if (ext
.strEquCI(".jpg") || ext
.strEquCI(".jpeg")) ximg
= readJpegFromMemory(data
);
97 else throw new Exception("unknown image format for '"~fname
~"'");
98 assert(ximg
!is null);
100 *img
= new EImage(ximg
);
105 static struct ImageInfo
{
109 ImageInfo
[$] imglist
= [
110 ImageInfo("main", &skinMainImg
),
111 ImageInfo("cbuttons", &skinMainCButtons
),
112 ImageInfo("shufrep", &skinMainShufRep
),
115 foreach (ref ii
; imglist
) { *(ii
.img
) = null; }
117 auto vid
= vfsAddPak(skinfile
, "skin:");
118 scope(exit
) vfsRemovePak(vid
);
119 foreach (auto de; vfsFileList
) {
120 if (!isGoodImageExtension(de.name
)) continue;
122 auto fname
= de.name
;
123 auto xpos
= fname
.lastIndexOf('/');
124 if (xpos
>= 0) fname
= fname
[xpos
+1..$];
125 else if (fname
.startsWith("skin:")) fname
= fname
[5..$];
126 xpos
= fname
.lastIndexOf('.');
127 if (xpos
<= 0) continue;
128 auto iname
= fname
[0..xpos
];
129 //conwriteln("[", de.name, "]:[", fname, "]:[", iname, "]");
131 foreach (ref ii
; imglist
) {
132 if (*(ii
.img
) is null && iname
.strEquCI(ii
.name
)) {
134 loadImage(ii
.img
, de.name
);
140 foreach (ref ii
; imglist
) if (*(ii
.img
) is null) throw new Exception("invalid skin (missing '"~ii
.name
~"' image)");
142 ampMain
= createAmpMain();
146 // ////////////////////////////////////////////////////////////////////////// //
152 this (MemoryImage img
) {
153 if (img
is null) throw new Exception("can't create eimage from nothing");
156 data
.length
= width
*height
;
157 { import core
.memory
: GC
; GC
.setAttr(data
.ptr
, GC
.BlkAttr
.NO_INTERIOR|GC
.BlkAttr
.NO_SCAN
); }
158 foreach (immutable int dy
; 0..img
.height
) {
159 foreach (immutable int dx
; 0..img
.width
) {
160 auto cc
= img
.getPixel(dx
, dy
);
161 data
[dy
*width
+dx
] = gxrgb(cc
.r
, cc
.g
, cc
.b
);
166 final void blitAt (int x
, int y
) nothrow @trusted @nogc {
168 if (x < 0 || y < 0 || x+width > VBufWidth || y+height > VBufHeight) return;
169 const(uint)* src = data.ptr;
170 uint* dst = vglTexBuf+y*VBufWidth+x;
171 immutable int w = width;
172 foreach (immutable dy; 0..height) {
173 dst[0..w] = src[0..w];
178 blitRect(x
, y
, GxRect(0, 0, width
, height
));
181 final void blitRect (int destx
, int desty
, GxRect srect
) nothrow @trusted @nogc {
182 srect
.intersect(GxRect(0, 0, width
, height
));
183 if (srect
.empty
) return;
184 assert(srect
.x0
>= 0 && srect
.x0
< width
);
185 assert(srect
.y0
>= 0 && srect
.y0
< height
);
186 assert(srect
.x1
>= 0 && srect
.x1
< width
);
187 assert(srect
.y1
>= 0 && srect
.y1
< height
);
190 int dw = srect
.width
;
191 int dh
= srect
.height
;
192 int skipLeft
, skipTop
;
193 if (!gxClipRect
.clipHVStripes(dx
, dy
, dw, dh
, &skipLeft
, &skipTop
)) return;
194 if (!GxRect(0, 0, VBufWidth
, VBufHeight
).clipHVStripes(dx
, dy
, dw, dh
, &skipLeft
, &skipTop
)) return;
195 assert(dw > 0 && dw <= width
);
196 assert(dh
> 0 && dh
<= height
);
197 assert(skipLeft
+srect
.x0
+dw <= width
);
198 assert(skipTop
+srect
.y0
+dh
<= height
);
199 const(uint)* src
= data
.ptr
+(skipTop
+srect
.y0
)*width
+(skipLeft
+srect
.x0
);
200 uint* dst
= vglTexBuf
+dy
*VBufWidth
+dx
;
202 assert(src
+dw <= data
.ptr
+width
*height
);
203 assert(dst
+dw <= vglTexBuf
+VBufWidth
*VBufHeight
);
204 dst
[0..dw] = src
[0..dw];
212 // ////////////////////////////////////////////////////////////////////////// //
217 int mActiveWidget
= -1;
220 if (aimg
is null) throw new Exception("no image for window");
222 imgrc
= GxRect(0, 0, img
.width
, img
.height
);
225 AmpWidget
addWidget (AmpWidget w
) {
226 if (w
is null) return null;
227 if (w
.parent
!is null) throw new Exception("widget already owned");
233 final @property AmpWidget
activeWidget () pure nothrow @safe @nogc { pragma(inline
, true); return (mActiveWidget
>= 0 && mActiveWidget
< widgets
.length ? widgets
[mActiveWidget
] : null); }
235 final @property void activeWidget (AmpWidget w
) pure nothrow @safe @nogc {
236 if (w
is null || w
.parent
!is this) { mActiveWidget
= -1; return; }
237 foreach (immutable idx
, AmpWidget ww
; widgets
) if (ww
is w
) { mActiveWidget
= cast(int)idx
; return; }
241 final AmpWidget
widgetAt (int x
, int y
) pure nothrow @safe @nogc {
242 foreach_reverse (AmpWidget w
; widgets
) {
243 if (w
.rc
.inside(x
, y
)) return w
;
250 foreach (AmpWidget w
; widgets
) w
.onPaint();
251 gxClipRect
= GxRect(109, 24, 157, 12);
252 gxDrawTextUtf(109, 24-2, "Testing text... жопф", gxRGB
!(0, 255, 0));
255 bool onKeyPre (KeyEvent event
) { return false; }
257 bool onKeyPost (KeyEvent event
) {
258 if (event
== "C-Q") { if (event
.pressed
) postQuitEvent(); return true; }
262 bool onKey (KeyEvent event
) {
263 if (onKeyPre(event
)) return true;
264 if (auto aw
= activeWidget
) {
265 if (aw
.onKey(event
)) return true;
267 if (onKeyPost(event
)) return true;
271 bool onMousePre (MouseEvent event
) { return false; }
272 bool onMousePost (MouseEvent event
) { return false; }
274 bool onMouse (MouseEvent event
) {
275 bool wasAwOrAct
= false;
276 if (onMousePre(event
)) return true;
277 if (auto aw
= activeWidget
) {
278 if (aw
.onMouse(event
)) return true;
281 if (auto ww
= widgetAt(event
.x
, event
.y
)) {
282 if (ww
.onMouse(event
)) return true;
285 if (onMousePost(event
)) return true;
286 if (wasAwOrAct
&& event
.type
!= MouseEventType
.motion
) return true;
292 // ////////////////////////////////////////////////////////////////////////// //
300 void delegate () onAction
;
304 this (EImage aimg
, int x
, int y
, GxRect aimgrc
, string acmd
=null) {
305 if (aimg
is null) throw new Exception("no image for window");
308 rc
= GxRect(x
, y
, aimgrc
.width
, aimgrc
.height
);
313 bool active
= (parent
!is null && parent
.activeWidget
is this);
315 if (active
) irc
.moveBy(0, imgrc
.height
);
316 img
.blitRect(rc
.x0
, rc
.y0
, irc
);
319 bool onKey (KeyEvent event
) {
323 bool onMouse (MouseEvent event
) {
324 if (event
.type
== MouseEventType
.buttonPressed
&& event
.button
== MouseButton
.left
) {
325 parent
.activeWidget
= this;
328 if (event
.type
== MouseEventType
.buttonReleased
&& event
.button
== MouseButton
.left
) {
329 if (parent
.activeWidget
is this) {
330 parent
.activeWidget
= null;
331 if (rc
.inside(event
.x
, event
.y
)) {
333 if (onAction
!is null) onAction();
343 // ////////////////////////////////////////////////////////////////////////// //
344 class AmpWidgetToggle
: AmpWidget
{
346 private bool checkedvar
;
348 this (bool* cvp
, EImage aimg
, int x
, int y
, GxRect aimgrc
, string acmd
=null) {
349 super(aimg
, x
, y
, aimgrc
, acmd
);
350 if (cvp
is null) cvp
= &checkedvar
;
354 override void onPaint () {
355 bool active
= (parent
!is null && parent
.activeWidget
is this);
357 if (*checked
) irc
.moveBy(0, imgrc
.height
*2);
358 if (active
) irc
.moveBy(0, imgrc
.height
);
359 img
.blitRect(rc
.x0
, rc
.y0
, irc
);