1- /// The base menu class for menus to inherit from.
2- /// Contains a mainFrame to put items in.
3- class MerchMenuGenericMenu : GenericMenu {
4- MerchMenuFrame mainFrame;
5- int mouseX;
6- int mouseY;
1+ /// Represents when in the element order the focus indicator element should be
2+ /// drawn.
3+ enum MerchZFFocusPriority {
4+ /// The focus indicator will be drawn above every element, besides things drawn explicitly on
5+ /// top.
6+ MerchZFFocusPriority_AboveAll,
7+ /// The focus indicator will be drawn immediately below the focused element.
8+ MerchZFFocusPriority_JustBelowFocused,
9+ /// The focus indicator will be drawn immediately above the focused element.
10+ MerchZFFocusPriority_JustAboveFocused,
11+ }
12+
13+ /// The class to inherit from for defining any menu that goes in the place that
14+ /// ZScript [`GenericMenu`]s normally go.
15+ class MerchZFGenericMenu : GenericMenu {
16+ /// The top-level frame of the menu's element tree.
17+ ///
18+ /// All top-level elements should be packed into this frame.
19+ MerchZFFrame mainFrame;
20+
21+ /// The default elements that are focused when the user hasn't got anything
22+ /// focused and presses a navigation button.
23+ MerchZFElement focusDefaults[MerchZFNavEventType_FocusChangeCount];
24+
25+ private MerchZFElementTreeGlobal globalStore;
26+
27+ /// Returns where the mouse position currently is in the menu.
28+ Vector2 getMousePos() { return globalStore.mousePos; }
29+
30+ /// Sets the currently focused element to be `elem`, as if it came from the
31+ /// direction `type`.
32+ void setFocus(MerchZFElement elem, MerchZFNavEventType type) {
33+ let old = globalStore.focus;
34+ globalStore.focus = elem;
35+ elem.beenFocused(type);
36+ changeFocusIndicator(old, globalStore.focus);
37+ if (globalStore.focusIndicator != NULL) globalStore.focusIndicator.show();
38+ }
39+ /// Returns the element that is drawn as an indicator of what element is
40+ /// focused.
41+ MerchZFElement getFocusIndicator() { return globalStore.focusIndicator; }
42+ /// Sets the element that is drawn as an indicator of what element is
43+ /// focused.
44+ void setFocusIndicator(MerchZFElement focusIndicator) { globalStore.focusIndicator = focusIndicator; }
45+
46+ /// Returns the focus indicator's priority.
47+ ///
48+ /// See [`ZF_FocusPriority`].
49+ MerchZFFocusPriority getFocusPriority() { return globalStore.focusPriority; }
50+ /// Sets the focus indicator's priority.
51+ ///
52+ /// See [`ZF_FocusPriority`].
53+ void setFocusPriority(MerchZFFocusPriority focusPriority) { globalStore.focusPriority = focusPriority; }
754
855 override void init(Menu parent) {
956 Super.init(parent);
10- mainFrame = new("MerchMenuFrame");
11- mainFrame.init((0, 0), (320, 200));
57+ mainFrame = MerchZFFrame.create((0, 0), (320, 200));
58+ globalStore = new("MerchZFElementTreeGlobal");
59+ mainFrame.setGlobalStore(globalStore);
1260 mainFrame.setBaseResolution((320, 200));
61+ globalStore.mainFrame = mainFrame;
62+ globalStore.needsMouseUpdate = true;
63+ setupFocusIndicator();
64+ if (globalStore.focusIndicator != NULL) {
65+ globalStore.focusIndicator.hide();
66+ }
1367 }
1468
69+ /// Sets the resolution of the virtual ZForms screen.
1570 void setBaseResolution(Vector2 size) {
1671 mainFrame.setBox((0, 0), size);
1772 mainFrame.setBaseResolution(size);
1873 }
1974
20- override void ticker() {
21- // Trigger a mouse movement UI event to fix things "sticking" sometimes
22- let zfEv = new("MerchMenuUiEvent");
23- zfEv.type = UIEvent.Type_MouseMove;
24- zfEv.mouseX = mouseX;
25- zfEv.mouseY = mouseY;
26- mainFrame.onUIEvent(zfEv);
75+ /// Called whenever the back button is pressed, so that its behaviour can
76+ /// be overridden if needed.
77+ ///
78+ /// Normally this gives all the elements in the tree a chance to override
79+ /// it, and if none of them did, it closes this menu.
80+ virtual void handleBack() {
81+ if (!mainFrame.handleBack()) {
82+ close();
83+ let m = GetCurrentMenu();
84+ MenuSound(m != null ? "menu/backup" : "menu/clear");
85+ if (!m) menuDelegate.MenuDismissed();
86+ }
87+ }
2788
89+ override bool translateKeyboardEvents() {
90+ if (globalStore == NULL) return true;
91+ return !globalStore.blockMenuEvent;
92+ }
93+
94+ override void ticker() {
2895 Super.ticker();
2996 mainFrame.ticker();
3097 }
3198
3299 override void drawer() {
33- Super.drawer();
100+ if (globalStore.focus != NULL) {
101+ MerchZFAABB box;
102+ globalStore.focus.getFocusAABB(box);
103+ positionFocusIndicator(box.pos, box.size);
104+ }
105+
106+ if (globalStore.needsMouseUpdate) {
107+ let mouseBlock = mainFrame.handlePriorityMouseBlock(false, globalStore.mousePos);
108+ mainFrame.handleMousePosition(mouseBlock, globalStore.mousePos);
109+ globalStore.needsMouseUpdate = false;
110+ }
34111 mainFrame.drawer();
112+ mainFrame.topDrawer();
113+ }
114+
115+ /// Called when the menu wants a focus indicator to be set up.
116+ ///
117+ /// # Examples
118+ ///
119+ /// ```
120+ /// override void setupFocusIndicator() {
121+ /// let focusBox = ZF_BoxTextures.CreateTexturePixels (
122+ /// "Graphics/FocusIndicator.png",
123+ /// (16, 16),
124+ /// (16, 16),
125+ /// true,
126+ /// true
127+ /// );
128+ ///
129+ /// setFocusIndicator(ZF_BoxImage.create((0, 0), (0, 0), focusBox));
130+ /// setFocusPriority(ZF_FocusPriority_JustAboveFocused);
131+ /// }
132+ /// ```
133+ virtual void setupFocusIndicator() {}
134+ /// Called when the menu wants the focus indicator to be moved to a new element.
135+ ///
136+ /// This should not position the indicator - see [`positionFocusIndicator`].
137+ virtual void changeFocusIndicator(MerchZFElement oldFocus, MerchZFElement newFocus) {}
138+ /// Called when the focus indicator should change position or size.
139+ ///
140+ /// # Examples
141+ ///
142+ /// ```
143+ /// override void positionFocusIndicator(Vector2 pos, Vector2 size) {
144+ /// getFocusIndicator().setBox(pos, size);
145+ /// }
146+ /// ```
147+ virtual void positionFocusIndicator(Vector2 pos, Vector2 size) {}
148+
149+ private void fireNavigationEvent(MerchZFNavEventType type, bool fromController) {
150+ if (type == MerchZFNavEventType_Deny) {
151+ handleBack();
152+ }
153+
154+ if (mainFrame.onNavEvent(type, fromController)) return;
155+
156+ if (type < MerchZFNavEventType_FocusChangeCount) {
157+ MerchZFElement newFocus;
158+ if (globalStore.focus == NULL) {
159+ newFocus = focusDefaults[type];
160+ } else {
161+ newFocus = globalStore.focus.getFocusNeighbor(type);
162+ }
163+ if (newFocus != NULL) {
164+ let oldFocus = globalStore.focus;
165+ setFocus(newFocus, type);
166+ }
167+ }
35168 }
36169
37170 // "relay" all UI events down to the elements so they can handle them
38171 override bool onUIEvent(UIEvent ev) {
172+ if (ev.type == UIEvent.Type_KeyDown && (ev.keyChar == UIEvent.Key_Escape || ev.keyChar == UIEvent.Key_Back)) {
173+ handleBack();
174+ }
39175 if (ev.type == UIEvent.Type_MouseMove) {
40- mouseX = ev.mouseX;
41- mouseY = ev.mouseY;
176+ globalStore.mousePos.x = ev.mouseX;
177+ globalStore.mousePos.y = ev.mouseY;
178+ globalStore.needsMouseUpdate = true;
179+ return true;
180+ }
181+ if (ev.type == UIEvent.Type_LButtonDown) {
182+ let oldFocus = globalStore.focus;
183+ globalStore.focus = NULL;
184+ changeFocusIndicator(oldFocus, NULL);
185+ if (globalStore.focusIndicator != NULL) globalStore.focusIndicator.hide();
186+ SetCapture(true);
187+ }
188+ if (ev.type == UIEvent.Type_LButtonUp) {
189+ SetCapture(false);
42190 }
43191
44- let zfEv = new("MerchMenuUiEvent") ;
192+ MerchZFUiEvent zfEv;
45193
46- MerchMenuUiEvent.fromGZDUiEvent(ev, zfEv);
47- mainFrame.onUIEvent(zfEv);
194+ MerchZFUiEvent.fromGZDUiEvent(ev, zfEv);
195+ if (mainFrame.onUIEventPriority(zfEv)) return true;
196+ if (mainFrame.onUIEvent(zfEv)) return true;
197+
198+ return true;
199+ }
200+
201+ override bool menuEvent(int mkey, bool fromcontroller) {
202+ if (globalStore != NULL && globalStore.blockMenuEvent) return true;
48203
49- return Super.onUIEvent(ev);
204+ switch (mkey) {
205+ case MKEY_Up: fireNavigationEvent(MerchZFNavEventType_Up, fromcontroller); break;
206+ case MKEY_Down: fireNavigationEvent(MerchZFNavEventType_Down, fromcontroller); break;
207+ case MKEY_Left: fireNavigationEvent(MerchZFNavEventType_Left, fromcontroller); break;
208+ case MKEY_Right: fireNavigationEvent(MerchZFNavEventType_Right, fromcontroller); break;
209+
210+ case MKEY_PageUp: fireNavigationEvent(MerchZFNavEventType_PageUp, fromcontroller); break;
211+ case MKEY_PageDown: fireNavigationEvent(MerchZFNavEventType_PageDown, fromcontroller); break;
212+
213+ case MKEY_Enter: fireNavigationEvent(MerchZFNavEventType_Confirm, fromcontroller); break;
214+ case MKEY_Back: fireNavigationEvent(MerchZFNavEventType_Deny, fromcontroller); break;
215+ }
216+ return true;
50217 }
51218}
52219
53- class MerchMenuEventHandlerMenu ui {
54- MerchMenuFrame mainFrame;
220+ // this is temporarily undocumented as i haven't been updating it to the
221+ // current ZForms API as much as i should have been so it doesn't work
222+ // properly anyway
223+ /// ?doc: hidden
224+ class MerchZFEventHandlerMenu ui {
225+ MerchZFFrame mainFrame;
55226
56227 protected bool useCustomCursor;
57228 protected string customCursor;
58229
59230 virtual void init() {
60- mainFrame = new("MerchMenuFrame");
61- mainFrame.init((0, 0), (320, 200));
231+ mainFrame = MerchZFFrame.create((0, 0), (320, 200));
62232 mainFrame.setBaseResolution((320, 200));
63233 }
64234
@@ -71,9 +241,13 @@ class MerchMenuEventHandlerMenu ui {
71241 ticker();
72242 }
73243
244+ virtual void handleBack() {
245+ mainFrame.handleBack();
246+ }
247+
74248 protected virtual void ticker() {
75249 // Trigger a mouse movement UI event to fix things "sticking" sometimes
76- let zfEv = new("MerchMenuUiEvent") ;
250+ MerchZFUiEvent zfEv;
77251 zfEv.type = UIEvent.Type_MouseMove;
78252 zfEv.mouseX = inputProc_MouseX;
79253 zfEv.mouseY = inputProc_MouseY;
@@ -92,16 +266,21 @@ class MerchMenuEventHandlerMenu ui {
92266
93267 protected virtual void drawer() {
94268 mainFrame.drawer();
269+ mainFrame.topDrawer();
95270 }
96271
97272 // "relay" all UI events down to the elements so they can handle them
98273 virtual bool onUIEvent(UIEvent ev) {
99- let zfEv = new("MerchMenuUiEvent");
274+ if (ev.type == UIEvent.Type_KeyDown && (ev.keyChar == UIEvent.Key_Escape || ev.keyChar == UIEvent.Key_Back)) {
275+ handleBack();
276+ }
100277
101- MerchMenuUiEvent.fromGZDUiEvent(ev, zfEv);
102- mainFrame.onUIEvent(zfEv);
278+ MerchZFUiEvent zfEv;
103279
104- return false;
280+ MerchZFUiEvent.fromGZDUiEvent(ev, zfEv);
281+ if (mainFrame.onUIEventPriority(zfEv)) return true;
282+ mainFrame.onUIEvent(zfEv);
283+ return true;
105284 }
106285
107286 private bool inputProc_Alt;
@@ -119,13 +298,13 @@ class MerchMenuEventHandlerMenu ui {
119298 inputProc_MouseY = posY;
120299 }
121300
122- void copyCustomMousePosition(MerchMenuEventHandlerMenu other) {
301+ void copyCustomMousePosition(MerchZFEventHandlerMenu other) {
123302 inputProc_MouseX = other.inputProc_MouseX;
124303 inputProc_MouseY = other.inputProc_MouseY;
125304 }
126305
127306 void onInputEvent(InputEvent gzdEv) {
128- let ev = new("MerchMenuUiEvent") ;
307+ MerchZFUiEvent ev;
129308
130309 ev.KeyString = gzdEv.KeyString;
131310 ev.KeyChar = gzdEv.KeyChar;
@@ -246,4 +425,4 @@ class MerchMenuEventHandlerMenu ui {
246425
247426 return -1;
248427 }
249- }
428+ }
0 commit comments