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
{
131 int curSectionNum
= 0;
132 int topSectionNum
= 0;
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) {
158 sections
.length
+= 1;
159 sections
[0].s
= atitle
;
160 textSize(atitle
, sections
[0].w
, sections
[0].h
);
161 maxW
= sections
[0].w
;
163 if (buildSections
!is null) {
164 auto list
= buildSections();
165 auto cpos
= sections
.length
;
166 sections
.length
+= list
.length
;
167 foreach (dstring s
; list
) {
168 sections
[cpos
].s
= s
;
169 textSize(s
, sections
[cpos
].w
, sections
[cpos
].h
);
170 if (maxW
< sections
[cpos
].w
) maxW
= sections
[cpos
].w
;
175 secTitle
= sections
[0];
176 sections
= sections
[1..$];
178 leftX
= (GWidth
-maxW
-Padding
*4)/2;
181 //topY = (GHeight-(ey-topY))/2;
186 int lastFullVisible () {
187 if (sections
.length
== 0) return 0;
188 int res
= topSectionNum
;
189 int y
= topY
+Padding
+secTitle
.h
;
190 int idx
= topSectionNum
;
191 if (idx
< 0) idx
= 0;
192 int ey
= calcBotY
-Padding
;
193 while (idx
< sections
.length
) {
194 y
+= sections
[idx
].h
;
195 if (y
> ey
) return res
;
199 return cast(int)sections
.length
-1;
203 int y
= topY
+Padding
*2+secTitle
.h
;
204 foreach (const ref sc
; sections
) {
206 if (ny
> GHeight
-Padding
*2) return y
;
213 if (curSectionNum
< 0) curSectionNum
= 0;
214 if (curSectionNum
>= sections
.length
) curSectionNum
= cast(int)sections
.length
-1;
215 if (sections
.length
) {
216 // make current item visible
217 if (curSectionNum
>= 0 && curSectionNum
< sections
.length
) {
218 if (curSectionNum
< topSectionNum
) {
219 topSectionNum
= curSectionNum
;
221 //FIXME: make this faster!
222 //writeln("top=", topSectionNum, "; cur=", curSectionNum, "; lv=", lastFullVisible);
223 while (lastFullVisible
< curSectionNum
) {
224 if (topSectionNum
>= sections
.length
-1) break;
230 while (topSectionNum
> 0) {
231 int lv
= lastFullVisible
;
233 int lv1
= lastFullVisible
;
234 if (lv1
!= lv
) { ++topSectionNum
; 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
, secTitle
.h
, -1, secTitle
.s
);
254 int idx
= topSectionNum
;
255 if (idx
< 0) idx
= 0;
257 while (idx
< sections
.length
) {
258 vg
.bndMenuItem(leftX
+Padding
, y
, maxW
, sections
[idx
].h
,
259 (curSectionNum
== idx ? BND_ACTIVE
: hoverItem
== idx ? BND_HOVER
: BND_DEFAULT
),
260 -1, sections
[idx
].s
);
261 y
+= sections
[idx
].h
;
268 enum int Close
= -666;
270 enum int NotMine
= -1;
272 int onKey (KeyEvent event
) {
273 if (!event
.pressed
) return NotMine
;
278 if (curSectionNum
> 0) {
284 if (curSectionNum
+1 < sections
.length
) {
290 if (sections
.length
) {
291 if (curSectionNum
== topSectionNum
) {
292 while (topSectionNum
> 0 && lastFullVisible
!= curSectionNum
) --topSectionNum
;
295 curSectionNum
= topSectionNum
;
299 if (sections
.length
) {
300 if (curSectionNum
== lastFullVisible
) {
301 while (lastFullVisible
< sections
.length
&& topSectionNum
!= curSectionNum
) ++topSectionNum
;
304 curSectionNum
= lastFullVisible
;
312 if (sections
.length
) {
313 curSectionNum
= cast(int)sections
.length
-1;
318 return (sections
.length ? curSectionNum
: 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
+= secTitle
.h
;
334 int idx
= topSectionNum
;
335 if (idx
< 0) idx
= 0;
336 while (idx
< sections
.length
) {
337 if (event
.y
>= y
&& event
.y
< y
+sections
[idx
].h
) { atItem
= idx
; break; }
338 y
+= sections
[idx
].h
;
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 < sections.length) return hoverItem;
360 if (hoverItem
>= 0 && hoverItem
< sections
.length
) {
361 curSectionNum
= hoverItem
;
369 if (event
.button
== MouseButton
.wheelUp
) {
370 //if (topSectionNum > 0) { --topSectionNum; needRedraw = true; }
371 if (curSectionNum
> 0) { --curSectionNum
; needRedraw
= true; }
373 if (event
.button
== MouseButton
.wheelDown
) {
374 //if (topSectionNum < sections.length) { ++topSectionNum; needRedraw = true; }
375 if (curSectionNum
< sections
.length
) { ++curSectionNum
; needRedraw
= true; }
378 case MouseEventType
.buttonReleased
:
379 if (event
.button
== MouseButton
.left
) {
380 //if (curSectionNum >= 0 && curSectionNum < sections.length) return curSectionNum;
381 if (curSectionNum
== hoverItem
&& curSectionNum
>= 0 && curSectionNum
< sections
.length
) return curSectionNum
;