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
;
25 import iv
.nanovg
.oui
.blendish
;
33 // ////////////////////////////////////////////////////////////////////////// //
34 void loadFonts (NVGContext vg
) {
35 void loadFont (ref int fid
, string name
, string path
) {
36 fid
= vg
.createFont(name
, path
);
37 if (fid
< 0) throw new Exception("can't load font '"~name
~"' from '"~path
~"'");
40 loadFont(textFont
, "text", textFontName
);
41 loadFont(textiFont
, "texti", textiFontName
);
42 loadFont(textbFont
, "textb", textbFontName
);
43 loadFont(textzFont
, "textz", textzFontName
);
45 loadFont(monoFont
, "mono", monoFontName
);
46 loadFont(monoiFont
, "monoi", monoiFontName
);
47 loadFont(monobFont
, "monob", monobFontName
);
48 loadFont(monozFont
, "monoz", monozFontName
);
50 loadFont(uiFont
, "ui", uiFontName
);
52 loadFont(galmapFont
, "galmap", galmapFontName
);
58 // ////////////////////////////////////////////////////////////////////////// //
59 // cursor (hi, Death Track!)
60 private enum curWidth
= 17;
61 private enum curHeight
= 23;
62 private static immutable ubyte[curWidth
*curHeight
] curImg
= [
63 0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
64 0,0,3,2,0,0,0,0,0,0,0,0,0,0,0,0,0,
65 1,0,3,2,2,0,0,0,0,0,0,0,0,0,0,0,0,
66 1,1,3,3,2,2,0,0,0,0,0,0,0,0,0,0,0,
67 1,1,3,3,4,2,2,0,0,0,0,0,0,0,0,0,0,
68 1,1,3,3,4,4,2,2,0,0,0,0,0,0,0,0,0,
69 1,1,3,3,4,4,4,2,2,0,0,0,0,0,0,0,0,
70 1,1,3,3,4,4,4,4,2,2,0,0,0,0,0,0,0,
71 1,1,3,3,4,4,4,5,6,2,2,0,0,0,0,0,0,
72 1,1,3,3,4,4,5,6,7,5,2,2,0,0,0,0,0,
73 1,1,3,3,4,5,6,7,5,4,5,2,2,0,0,0,0,
74 1,1,3,3,5,6,7,5,4,5,6,7,2,2,0,0,0,
75 1,1,3,3,6,7,5,4,5,6,7,7,7,2,2,0,0,
76 1,1,3,3,7,5,4,5,6,7,7,7,7,7,2,2,0,
77 1,1,3,3,5,4,5,6,8,8,8,8,8,8,8,8,2,
78 1,1,3,3,4,5,6,3,8,8,8,8,8,8,8,8,8,
79 1,1,3,3,5,6,3,3,1,1,1,1,1,1,1,0,0,
80 1,1,3,3,6,3,3,1,1,1,1,1,1,1,1,0,0,
81 1,1,3,3,3,3,0,0,0,0,0,0,0,0,0,0,0,
82 1,1,3,3,3,0,0,0,0,0,0,0,0,0,0,0,0,
83 1,1,3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,
84 1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
85 1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
87 struct CC
{ ubyte r
, g
, b
, a
; }
88 private static immutable CC
[9] curPal
= [
101 int createCursorImage (NVGContext vg
) {
102 auto img
= new ubyte[](curWidth
*curHeight
*4);
103 scope(exit
) img
.destroy
;
104 const(ubyte)* s
= curImg
.ptr
;
106 foreach (immutable _
; 0..curWidth
*curHeight
) {
107 auto c
= curPal
.ptr
[*s
++];
113 return vg
.createImageRGBA(curWidth
, curHeight
, NVGImageFlags
.None
, img
[]);
117 // ////////////////////////////////////////////////////////////////////////// //
118 final class PopupMenu
{
123 static struct SecInfo
{
135 int maxW
; // maximum text width
140 void textSize (dstring s
, out int tw
, out int th
) {
141 auto w
= vg
.bndLabelWidth(-1, s
)+4;
142 if (w
> GWidth
-Padding
*4) w
= GWidth
-Padding
*4;
143 auto h
= vg
.bndLabelHeight(-1, s
, w
);
144 if (h
< BND_WIDGET_HEIGHT
) h
= BND_WIDGET_HEIGHT
;
149 this (NVGContext avg
, dstring atitle
, scope dstring
[] delegate () buildSections
) {
150 assert(avg
!is null);
152 vg
.fontFaceId(uiFont
);
153 vg
.textAlign(NVGAlign
.Left|NVGAlign
.Baseline
);
154 vg
.fontSize(fsizeUI
);
156 if (atitle
!is null) {
160 textSize(atitle
, items
[0].w
, items
[0].h
);
163 if (buildSections
!is null) {
164 auto list
= buildSections();
165 auto cpos
= items
.length
;
166 items
.length
+= list
.length
;
167 foreach (dstring s
; list
) {
169 textSize(s
, items
[cpos
].w
, items
[cpos
].h
);
170 if (maxW
< items
[cpos
].w
) maxW
= items
[cpos
].w
;
175 menuTitle
= items
[0];
178 leftX
= (GWidth
-maxW
-Padding
*4)/2;
181 //topY = (GHeight-(ey-topY))/2;
186 int lastFullVisible () {
187 if (items
.length
== 0) return 0;
188 int res
= topItemIdx
;
189 int y
= topY
+Padding
+menuTitle
.h
;
190 int idx
= topItemIdx
;
191 if (idx
< 0) idx
= 0;
192 int ey
= calcBotY
-Padding
;
193 while (idx
< items
.length
) {
195 if (y
> ey
) return res
;
199 return cast(int)items
.length
-1;
203 int y
= topY
+Padding
*2+menuTitle
.h
;
204 foreach (const ref sc
; items
) {
206 if (ny
> GHeight
-Padding
*2) return y
;
213 if (curItemIdx
< 0) curItemIdx
= 0;
214 if (curItemIdx
>= items
.length
) curItemIdx
= cast(int)items
.length
-1;
216 // make current item visible
217 if (curItemIdx
>= 0 && curItemIdx
< items
.length
) {
218 if (curItemIdx
< topItemIdx
) {
219 topItemIdx
= curItemIdx
;
221 //FIXME: make this faster!
222 //writeln("top=", topItemIdx, "; cur=", curItemIdx, "; lv=", lastFullVisible);
223 while (lastFullVisible
< curItemIdx
) {
224 if (topItemIdx
>= items
.length
-1) break;
230 while (topItemIdx
> 0) {
231 int lv
= lastFullVisible
;
233 int lv1
= lastFullVisible
;
234 if (lv1
!= lv
) { ++topItemIdx
; break; }
240 scope(exit
) vg
.restore();
241 if (moving
) vg
.globalAlpha(0.8);
242 vg
.fontFaceId(uiFont
);
243 vg
.textAlign(NVGAlign
.Left|NVGAlign
.Baseline
);
244 vg
.fontSize(fsizeUI
);
247 vg
.bndMenuBackground(leftX
, topY
, maxW
+Padding
*2, ey
-topY
, BND_CORNER_NONE
);
248 int y
= topY
+Padding
;
249 vg
.scissor(leftX
+Padding
, y
, maxW
, ey
-Padding
);
251 vg
.bndMenuLabel(leftX
+Padding
, y
, maxW
, menuTitle
.h
, -1, menuTitle
.s
);
254 int idx
= topItemIdx
;
255 if (idx
< 0) idx
= 0;
257 while (idx
< items
.length
) {
258 vg
.bndMenuItem(leftX
+Padding
, y
, maxW
, items
[idx
].h
,
259 (curItemIdx
== idx ? BND_ACTIVE
: hoverItem
== idx ? BND_HOVER
: BND_DEFAULT
),
268 enum int Close
= -666;
270 enum int NotMine
= -1;
272 int onKey (KeyEvent event
) {
273 if (!event
.pressed
) return NotMine
;
278 if (curItemIdx
> 0) {
284 if (curItemIdx
+1 < items
.length
) {
291 if (curItemIdx
== topItemIdx
) {
292 while (topItemIdx
> 0 && lastFullVisible
!= curItemIdx
) --topItemIdx
;
295 curItemIdx
= topItemIdx
;
300 if (curItemIdx
== lastFullVisible
) {
301 while (lastFullVisible
< items
.length
&& topItemIdx
!= curItemIdx
) ++topItemIdx
;
304 curItemIdx
= lastFullVisible
;
313 curItemIdx
= cast(int)items
.length
-1;
318 return (items
.length ? curItemIdx
: Close
);
324 int onMouse (MouseEvent event
) {
327 if (event
.x
>= leftX
+Padding
&& event
.x
< leftX
+Padding
+maxW
&& event
.y
>= topY
+Padding
) {
329 int ey
= calcBotY
-Padding
;
332 int y
= topY
+Padding
;
333 if (hasTitle
) y
+= menuTitle
.h
;
334 int idx
= topItemIdx
;
335 if (idx
< 0) idx
= 0;
336 while (idx
< items
.length
) {
337 if (event
.y
>= y
&& event
.y
< y
+items
[idx
].h
) { atItem
= idx
; break; }
344 if (hoverItem
!= atItem
) {
348 switch (event
.type
) {
349 case MouseEventType
.motion
:
356 case MouseEventType
.buttonPressed
:
357 if (event
.button
== MouseButton
.right
) return Close
;
358 if (event
.button
== MouseButton
.left
) {
359 //if (hoverItem >= 0 && hoverItem < items.length) return hoverItem;
360 if (hoverItem
>= 0 && hoverItem
< items
.length
) {
361 curItemIdx
= hoverItem
;
369 if (event
.button
== MouseButton
.wheelUp
) {
370 //if (topItemIdx > 0) { --topItemIdx; needRedraw = true; }
371 if (curItemIdx
> 0) { --curItemIdx
; needRedraw
= true; }
373 if (event
.button
== MouseButton
.wheelDown
) {
374 //if (topItemIdx < items.length) { ++topItemIdx; needRedraw = true; }
375 if (curItemIdx
< items
.length
) { ++curItemIdx
; needRedraw
= true; }
378 case MouseEventType
.buttonReleased
:
379 if (event
.button
== MouseButton
.left
) {
380 //if (curItemIdx >= 0 && curItemIdx < items.length) return curItemIdx;
381 if (curItemIdx
== hoverItem
&& curItemIdx
>= 0 && curItemIdx
< items
.length
) return curItemIdx
;