1 /* Written 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/>.
19 import arsd
.simpledisplay
;
23 import iv
.nanovg
.oui
.blendish
;
31 // ////////////////////////////////////////////////////////////////////////// //
32 void loadFonts (NVGContext vg
) {
33 void loadFont (ref int fid
, string name
, string path
) {
34 fid
= vg
.createFont(name
, path
);
35 if (fid
< 0) throw new Exception("can't load font '"~name
~"' from '"~path
~"'");
38 loadFont(textFont
, "text", textFontName
);
39 loadFont(textiFont
, "texti", textiFontName
);
40 loadFont(textbFont
, "textb", textbFontName
);
41 loadFont(textzFont
, "textz", textzFontName
);
43 loadFont(monoFont
, "mono", monoFontName
);
44 loadFont(monoiFont
, "monoi", monoiFontName
);
45 loadFont(monobFont
, "monob", monobFontName
);
46 loadFont(monozFont
, "monoz", monozFontName
);
48 loadFont(uiFont
, "ui", uiFontName
);
50 loadFont(galmapFont
, "galmap", galmapFontName
);
56 // ////////////////////////////////////////////////////////////////////////// //
57 // cursor (hi, Death Track!)
58 private enum curWidth
= 17;
59 private enum curHeight
= 23;
60 private static immutable ubyte[curWidth
*curHeight
] curImg
= [
61 0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
62 0,0,3,2,0,0,0,0,0,0,0,0,0,0,0,0,0,
63 1,0,3,2,2,0,0,0,0,0,0,0,0,0,0,0,0,
64 1,1,3,3,2,2,0,0,0,0,0,0,0,0,0,0,0,
65 1,1,3,3,4,2,2,0,0,0,0,0,0,0,0,0,0,
66 1,1,3,3,4,4,2,2,0,0,0,0,0,0,0,0,0,
67 1,1,3,3,4,4,4,2,2,0,0,0,0,0,0,0,0,
68 1,1,3,3,4,4,4,4,2,2,0,0,0,0,0,0,0,
69 1,1,3,3,4,4,4,5,6,2,2,0,0,0,0,0,0,
70 1,1,3,3,4,4,5,6,7,5,2,2,0,0,0,0,0,
71 1,1,3,3,4,5,6,7,5,4,5,2,2,0,0,0,0,
72 1,1,3,3,5,6,7,5,4,5,6,7,2,2,0,0,0,
73 1,1,3,3,6,7,5,4,5,6,7,7,7,2,2,0,0,
74 1,1,3,3,7,5,4,5,6,7,7,7,7,7,2,2,0,
75 1,1,3,3,5,4,5,6,8,8,8,8,8,8,8,8,2,
76 1,1,3,3,4,5,6,3,8,8,8,8,8,8,8,8,8,
77 1,1,3,3,5,6,3,3,1,1,1,1,1,1,1,0,0,
78 1,1,3,3,6,3,3,1,1,1,1,1,1,1,1,0,0,
79 1,1,3,3,3,3,0,0,0,0,0,0,0,0,0,0,0,
80 1,1,3,3,3,0,0,0,0,0,0,0,0,0,0,0,0,
81 1,1,3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,
82 1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
83 1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
85 struct CC
{ ubyte r
, g
, b
, a
; }
86 private static immutable CC
[9] curPal
= [
99 int createCursorImage (NVGContext vg
, bool white
=false) {
100 auto img
= new ubyte[](curWidth
*curHeight
*4);
101 scope(exit
) img
.destroy
;
102 const(ubyte)* s
= curImg
.ptr
;
104 foreach (immutable _
; 0..curWidth
*curHeight
) {
105 CC c
= curPal
.ptr
[*s
++];
108 //c = CC(255, 127, 0, 255);
109 auto hsl
= nvgRGB(c
.r
, c
.g
, c
.b
).asHSL
;
110 //hsl.l *= 1.25; if (hsl.l > 0.8) hsl.l = 0.8;
111 //hsl.l *= 1.25; if (hsl.l > 1) hsl.l = 1;
112 //hsl.h *= 2.25; if (hsl.h > 1) hsl.h = 1;
114 auto cc
= hsl
.asColor
.asUint
;
115 *d
++ = cc
&0xff; cc
>>= 8;
116 *d
++ = cc
&0xff; cc
>>= 8;
117 *d
++ = cc
&0xff; cc
>>= 8;
118 *d
++ = cc
&0xff; cc
>>= 8;
132 return vg
.createImageRGBA(curWidth
, curHeight
, img
[]);
136 // ////////////////////////////////////////////////////////////////////////// //
137 final class PopupMenu
{
142 static struct SecInfo
{
154 int maxW
; // maximum text width
159 void textSize (dstring s
, out int tw
, out int th
) {
160 auto w
= vg
.bndLabelWidth(-1, s
)+4;
161 if (w
> GWidth
-Padding
*4) w
= GWidth
-Padding
*4;
162 auto h
= vg
.bndLabelHeight(-1, s
, w
);
163 if (h
< BND_WIDGET_HEIGHT
) h
= BND_WIDGET_HEIGHT
;
168 this (NVGContext avg
, dstring atitle
, scope dstring
[] delegate () buildSections
) {
169 assert(avg
!is null);
171 vg
.fontFaceId(uiFont
);
172 vg
.textAlign(NVGTextAlign
.H
.Left
, NVGTextAlign
.V
.Baseline
);
173 vg
.fontSize(fsizeUI
);
175 if (atitle
!is null) {
179 textSize(atitle
, items
[0].w
, items
[0].h
);
182 if (buildSections
!is null) {
183 auto list
= buildSections();
184 auto cpos
= items
.length
;
185 items
.length
+= list
.length
;
186 foreach (dstring s
; list
) {
188 textSize(s
, items
[cpos
].w
, items
[cpos
].h
);
189 if (maxW
< items
[cpos
].w
) maxW
= items
[cpos
].w
;
194 menuTitle
= items
[0];
197 leftX
= (GWidth
-maxW
-Padding
*4)/2;
200 //topY = (GHeight-(ey-topY))/2;
205 int lastFullVisible () {
206 if (items
.length
== 0) return 0;
207 int res
= topItemIdx
;
208 int y
= topY
+Padding
+menuTitle
.h
;
209 int idx
= topItemIdx
;
210 if (idx
< 0) idx
= 0;
211 int ey
= calcBotY
-Padding
;
212 while (idx
< items
.length
) {
214 if (y
> ey
) return res
;
218 return cast(int)items
.length
-1;
222 int y
= topY
+Padding
*2+menuTitle
.h
;
223 foreach (const ref sc
; items
) {
225 if (ny
> GHeight
-Padding
*2) return y
;
232 if (curItemIdx
< 0) curItemIdx
= 0;
233 if (curItemIdx
>= items
.length
) curItemIdx
= cast(int)items
.length
-1;
235 // make current item visible
236 if (curItemIdx
>= 0 && curItemIdx
< items
.length
) {
237 if (curItemIdx
< topItemIdx
) {
238 topItemIdx
= curItemIdx
;
240 //FIXME: make this faster!
241 //writeln("top=", topItemIdx, "; cur=", curItemIdx, "; lv=", lastFullVisible);
242 while (lastFullVisible
< curItemIdx
) {
243 if (topItemIdx
>= items
.length
-1) break;
249 while (topItemIdx
> 0) {
250 int lv
= lastFullVisible
;
252 int lv1
= lastFullVisible
;
253 if (lv1
!= lv
) { ++topItemIdx
; break; }
259 scope(exit
) vg
.restore();
260 if (moving
) vg
.globalAlpha(0.8);
261 vg
.fontFaceId(uiFont
);
262 vg
.textAlign(NVGTextAlign
.H
.Left
, NVGTextAlign
.V
.Baseline
);
263 vg
.fontSize(fsizeUI
);
266 vg
.bndMenuBackground(leftX
, topY
, maxW
+Padding
*2, ey
-topY
, BND_CORNER_NONE
);
267 int y
= topY
+Padding
;
268 vg
.scissor(leftX
+Padding
, y
, maxW
, ey
-Padding
);
270 vg
.bndMenuLabel(leftX
+Padding
, y
, maxW
, menuTitle
.h
, -1, menuTitle
.s
);
273 int idx
= topItemIdx
;
274 if (idx
< 0) idx
= 0;
276 while (idx
< items
.length
) {
277 vg
.bndMenuItem(leftX
+Padding
, y
, maxW
, items
[idx
].h
,
278 (curItemIdx
== idx ? BND_ACTIVE
: hoverItem
== idx ? BND_HOVER
: BND_DEFAULT
),
287 enum int Close
= -666;
289 enum int NotMine
= -1;
291 int onKey (KeyEvent event
) {
292 if (!event
.pressed
) return NotMine
;
297 if (curItemIdx
> 0) {
303 if (curItemIdx
+1 < items
.length
) {
310 if (curItemIdx
== topItemIdx
) {
311 while (topItemIdx
> 0 && lastFullVisible
!= curItemIdx
) --topItemIdx
;
314 curItemIdx
= topItemIdx
;
319 if (curItemIdx
== lastFullVisible
) {
320 while (lastFullVisible
< items
.length
&& topItemIdx
!= curItemIdx
) ++topItemIdx
;
323 curItemIdx
= lastFullVisible
;
332 curItemIdx
= cast(int)items
.length
-1;
337 return (items
.length ? curItemIdx
: Close
);
343 int onMouse (MouseEvent event
) {
346 if (event
.x
>= leftX
+Padding
&& event
.x
< leftX
+Padding
+maxW
&& event
.y
>= topY
+Padding
) {
348 int ey
= calcBotY
-Padding
;
351 int y
= topY
+Padding
;
352 if (hasTitle
) y
+= menuTitle
.h
;
353 int idx
= topItemIdx
;
354 if (idx
< 0) idx
= 0;
355 while (idx
< items
.length
) {
356 if (event
.y
>= y
&& event
.y
< y
+items
[idx
].h
) { atItem
= idx
; break; }
363 if (hoverItem
!= atItem
) {
367 switch (event
.type
) {
368 case MouseEventType
.motion
:
375 case MouseEventType
.buttonPressed
:
376 if (event
.button
== MouseButton
.right
) return Close
;
377 if (event
.button
== MouseButton
.left
) {
378 //if (hoverItem >= 0 && hoverItem < items.length) return hoverItem;
379 if (hoverItem
>= 0 && hoverItem
< items
.length
) {
380 curItemIdx
= hoverItem
;
388 if (event
.button
== MouseButton
.wheelUp
) {
389 //if (topItemIdx > 0) { --topItemIdx; needRedraw = true; }
390 if (curItemIdx
> 0) { --curItemIdx
; needRedraw
= true; }
392 if (event
.button
== MouseButton
.wheelDown
) {
393 //if (topItemIdx < items.length) { ++topItemIdx; needRedraw = true; }
394 if (curItemIdx
< items
.length
) { ++curItemIdx
; needRedraw
= true; }
397 case MouseEventType
.buttonReleased
:
398 if (event
.button
== MouseButton
.left
) {
399 //if (curItemIdx >= 0 && curItemIdx < items.length) return curItemIdx;
400 if (curItemIdx
== hoverItem
&& curItemIdx
>= 0 && curItemIdx
< items
.length
) return curItemIdx
;