2020-04-24 07:27:02 -04:00
|
|
|
#include "common.h"
|
|
|
|
#include "RwHelper.h"
|
|
|
|
#include "Pad.h"
|
|
|
|
#include "ControllerConfig.h"
|
|
|
|
#include "Timer.h"
|
|
|
|
#include "rtcharse.h"
|
|
|
|
#include "inttypes.h"
|
|
|
|
#include "debugmenu.h"
|
2020-05-01 11:31:29 -04:00
|
|
|
#include <new>
|
2020-04-24 07:27:02 -04:00
|
|
|
|
|
|
|
#define snprintf _snprintf
|
|
|
|
|
|
|
|
#define strdup _strdup
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Font stuff
|
|
|
|
struct Pt
|
|
|
|
{
|
|
|
|
int x, y;
|
|
|
|
};
|
|
|
|
|
|
|
|
enum MenuFontStyle
|
|
|
|
{
|
|
|
|
MENUFONT_NORMAL,
|
|
|
|
MENUFONT_SEL_ACTIVE,
|
|
|
|
MENUFONT_SEL_INACTIVE,
|
|
|
|
MENUFONT_MOUSE
|
|
|
|
};
|
|
|
|
|
|
|
|
RtCharset *fontStyles[4];
|
|
|
|
RtCharsetDesc fontDesc;
|
|
|
|
int fontscale = 1; // not supported right now
|
|
|
|
|
|
|
|
Pt
|
|
|
|
fontGetStringSize(const char *s)
|
|
|
|
{
|
|
|
|
Pt sz = { 0, 0 };
|
|
|
|
int x;
|
|
|
|
char c;
|
|
|
|
sz.y = fontDesc.height*fontscale; // always assume one line;
|
|
|
|
x = 0;
|
|
|
|
while(c = *s++){
|
|
|
|
if(c == '\n'){
|
|
|
|
sz.y += fontDesc.height*fontscale;
|
|
|
|
if(x > sz.x)
|
|
|
|
sz.x = x;
|
|
|
|
x = 0;
|
|
|
|
}else
|
|
|
|
x += fontDesc.width*fontscale;
|
|
|
|
}
|
|
|
|
if(x > sz.x)
|
|
|
|
sz.x = x;
|
|
|
|
return sz;
|
|
|
|
}
|
|
|
|
|
|
|
|
Pt
|
|
|
|
fontPrint(const char *s, float x, float y, int style)
|
|
|
|
{
|
|
|
|
RtCharsetPrintBuffered(fontStyles[style], s, x, y, false);
|
|
|
|
return fontGetStringSize(s);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
fontGetLen(int len)
|
|
|
|
{
|
|
|
|
return len*fontDesc.width*fontscale;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
createMenuFont(void)
|
|
|
|
{
|
|
|
|
OpenCharsetSafe();
|
|
|
|
|
|
|
|
RwRGBA fg_normal = { 255, 255, 255, 255 };
|
|
|
|
RwRGBA bg_normal = { 255, 255, 255, 0 };
|
|
|
|
fontStyles[MENUFONT_NORMAL] = RtCharsetCreate(&fg_normal, &bg_normal);
|
|
|
|
assert(fontStyles[MENUFONT_NORMAL]);
|
|
|
|
|
|
|
|
RwRGBA fg_sel_active = { 200, 200, 200, 255 };
|
|
|
|
RwRGBA bg_sel_active = { 132, 132, 132, 255 };
|
|
|
|
fontStyles[MENUFONT_SEL_ACTIVE] = RtCharsetCreate(&fg_sel_active, &bg_sel_active);
|
|
|
|
assert(fontStyles[MENUFONT_SEL_ACTIVE]);
|
|
|
|
|
|
|
|
RwRGBA fg_sel_inactive = { 200, 200, 200, 255 };
|
|
|
|
RwRGBA bg_sel_inactive = { 200, 200, 200, 0 };
|
|
|
|
fontStyles[MENUFONT_SEL_INACTIVE] = RtCharsetCreate(&fg_sel_inactive, &bg_sel_inactive);
|
|
|
|
assert(fontStyles[MENUFONT_SEL_INACTIVE]);
|
|
|
|
|
|
|
|
RwRGBA fg_mouse = { 255, 255, 255, 255 };
|
|
|
|
RwRGBA bg_mouse = { 132, 132, 132, 255 };
|
|
|
|
fontStyles[MENUFONT_MOUSE] = RtCharsetCreate(&fg_mouse, &bg_mouse);
|
|
|
|
assert(fontStyles[MENUFONT_MOUSE]);
|
|
|
|
|
|
|
|
RtCharsetGetDesc(fontStyles[MENUFONT_NORMAL], &fontDesc);
|
|
|
|
}
|
|
|
|
|
2020-04-24 18:40:14 -04:00
|
|
|
void
|
|
|
|
destroyMenuFont(void)
|
|
|
|
{
|
|
|
|
RtCharsetDestroy(fontStyles[MENUFONT_NORMAL]);
|
|
|
|
fontStyles[MENUFONT_NORMAL] = nil;
|
|
|
|
RtCharsetDestroy(fontStyles[MENUFONT_SEL_ACTIVE]);
|
|
|
|
fontStyles[MENUFONT_SEL_ACTIVE] = nil;
|
|
|
|
RtCharsetDestroy(fontStyles[MENUFONT_SEL_INACTIVE]);
|
|
|
|
fontStyles[MENUFONT_SEL_INACTIVE] = nil;
|
|
|
|
RtCharsetDestroy(fontStyles[MENUFONT_MOUSE]);
|
|
|
|
fontStyles[MENUFONT_MOUSE] = nil;
|
|
|
|
}
|
2020-04-24 07:27:02 -04:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
enum EntryType
|
|
|
|
{
|
|
|
|
MENUEMPTY = 0,
|
|
|
|
MENUSUB,
|
|
|
|
MENUVAR,
|
|
|
|
|
|
|
|
MENUVAR_INT,
|
|
|
|
MENUVAR_FLOAT,
|
|
|
|
MENUVAR_CMD,
|
|
|
|
|
|
|
|
MENUSCROLL // dummy
|
|
|
|
};
|
|
|
|
|
|
|
|
struct Menu
|
|
|
|
{
|
|
|
|
Menu *parent;
|
|
|
|
RwRect r;
|
|
|
|
MenuEntry *entries;
|
|
|
|
int numEntries;
|
|
|
|
int maxNameWidth, maxValWidth;
|
|
|
|
|
|
|
|
MenuEntry *findEntry(const char *entryname);
|
|
|
|
void insertEntrySorted(MenuEntry *entry);
|
|
|
|
void appendEntry(MenuEntry *entry);
|
|
|
|
|
|
|
|
bool isScrollingUp, isScrollingDown;
|
|
|
|
int scrollStart;
|
|
|
|
int numVisible;
|
|
|
|
RwRect scrollUpR, scrollDownR;
|
|
|
|
void scroll(int off);
|
|
|
|
|
|
|
|
int selection;
|
|
|
|
MenuEntry *selectedEntry; // updated by update
|
|
|
|
void changeSelection(int newsel);
|
|
|
|
void changeSelection(MenuEntry *e);
|
|
|
|
|
|
|
|
void update(void);
|
|
|
|
void draw(void);
|
|
|
|
Menu(void){ memset(this, 0, sizeof(Menu)); }
|
2020-05-01 11:31:29 -04:00
|
|
|
~Menu(void);
|
2020-04-24 07:27:02 -04:00
|
|
|
};
|
|
|
|
extern Menu toplevel;
|
|
|
|
|
|
|
|
struct MenuEntry_Sub : MenuEntry
|
|
|
|
{
|
|
|
|
Menu *submenu;
|
|
|
|
|
|
|
|
MenuEntry_Sub(const char *name, Menu *menu);
|
2020-05-01 11:31:29 -04:00
|
|
|
~MenuEntry_Sub(void) { delete submenu; }
|
2020-04-24 07:27:02 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
struct MenuEntry_Var : MenuEntry
|
|
|
|
{
|
|
|
|
int maxvallen;
|
|
|
|
int vartype;
|
|
|
|
bool wrapAround;
|
|
|
|
|
|
|
|
virtual void processInput(bool mouseOver, bool selected) = 0;
|
|
|
|
int getValWidth(void) { return maxvallen; }
|
|
|
|
virtual void getValStr(char *str, int len) = 0;
|
|
|
|
MenuEntry_Var(const char *name, int type);
|
|
|
|
};
|
|
|
|
|
|
|
|
struct MenuEntry_Int : MenuEntry_Var
|
|
|
|
{
|
|
|
|
virtual void setStrings(const char **strings) = 0;
|
|
|
|
virtual int findStringLen(void) = 0;
|
|
|
|
MenuEntry_Int(const char *name);
|
|
|
|
};
|
|
|
|
|
|
|
|
#define INTTYPES \
|
|
|
|
X(Int8, int8, 4, "%4" PRId8) \
|
|
|
|
X(Int16, int16, 6, "%6" PRId16) \
|
|
|
|
X(Int32, int32, 11, "%11" PRId32) \
|
|
|
|
X(Int64, int64, 21, "%21" PRId64) \
|
|
|
|
X(UInt8, uint8, 4, "%4" PRIu8) \
|
|
|
|
X(UInt16, uint16, 6, "%6" PRIu16) \
|
|
|
|
X(UInt32, uint32, 11, "%11" PRIu32) \
|
|
|
|
X(UInt64, uint64, 21, "%21" PRIu64)
|
|
|
|
#define FLOATTYPES \
|
|
|
|
X(Float32, float, 11, "%11.3f") \
|
|
|
|
X(Float64, double, 11, "%11.3lf")
|
|
|
|
|
|
|
|
#define X(NAME, TYPE, MAXLEN, FMT) \
|
|
|
|
struct MenuEntry_##NAME : MenuEntry_Int \
|
|
|
|
{ \
|
|
|
|
TYPE *variable; \
|
|
|
|
TYPE lowerBound, upperBound; \
|
|
|
|
TYPE step; \
|
|
|
|
TriggerFunc triggerFunc; \
|
|
|
|
const char *fmt; \
|
|
|
|
const char **strings; \
|
|
|
|
\
|
|
|
|
void processInput(bool mouseOver, bool selected); \
|
|
|
|
void getValStr(char *str, int len); \
|
|
|
|
\
|
|
|
|
void setStrings(const char **strings); \
|
|
|
|
int findStringLen(void); \
|
|
|
|
MenuEntry_##NAME(const char *name, TYPE *ptr, TriggerFunc triggerFunc, TYPE step, TYPE lowerBound, TYPE upperBound, const char **strings); \
|
|
|
|
};
|
|
|
|
INTTYPES
|
|
|
|
#undef X
|
|
|
|
|
|
|
|
#define X(NAME, TYPE, MAXLEN, FMT) \
|
|
|
|
struct MenuEntry_##NAME : MenuEntry_Var \
|
|
|
|
{ \
|
|
|
|
TYPE *variable; \
|
|
|
|
TYPE lowerBound, upperBound; \
|
|
|
|
TYPE step; \
|
|
|
|
TriggerFunc triggerFunc; \
|
|
|
|
const char *fmt; \
|
|
|
|
\
|
|
|
|
void processInput(bool mouseOver, bool selected); \
|
|
|
|
void getValStr(char *str, int len); \
|
|
|
|
\
|
|
|
|
MenuEntry_##NAME(const char *name, TYPE *ptr, TriggerFunc triggerFunc, TYPE step, TYPE lowerBound, TYPE upperBound); \
|
|
|
|
};
|
|
|
|
FLOATTYPES
|
|
|
|
#undef X
|
|
|
|
|
|
|
|
struct MenuEntry_Cmd : MenuEntry_Var
|
|
|
|
{
|
|
|
|
TriggerFunc triggerFunc;
|
|
|
|
|
|
|
|
void processInput(bool mouseOver, bool selected);
|
|
|
|
void getValStr(char *str, int len);
|
|
|
|
|
|
|
|
MenuEntry_Cmd(const char *name, TriggerFunc triggerFunc);
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
Menu *findMenu(const char *name);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#define MUHKEYS \
|
|
|
|
X(leftjustdown, rsLEFT) \
|
|
|
|
X(rightjustdown, rsRIGHT) \
|
|
|
|
X(upjustdown, rsUP) \
|
|
|
|
X(downjustdown, rsDOWN) \
|
|
|
|
X(pgupjustdown, rsPGUP) \
|
|
|
|
X(pgdnjustdown, rsPGDN)
|
|
|
|
|
|
|
|
#define MUHBUTTONS \
|
|
|
|
X(button1justdown, 1) \
|
|
|
|
X(button2justdown, 2) \
|
|
|
|
X(button3justdown, 3)
|
|
|
|
|
|
|
|
#define REPEATDELAY 700
|
|
|
|
#define REPEATINTERVAL 50
|
|
|
|
#define X(var, keycode) static int var;
|
|
|
|
MUHKEYS
|
|
|
|
#undef X
|
|
|
|
static int downtime;
|
|
|
|
static int repeattime;
|
|
|
|
static int lastkeydown;
|
|
|
|
static int *keyptr;
|
|
|
|
|
|
|
|
static int buttondown[3];
|
|
|
|
static int lastbuttondown;
|
|
|
|
static int *buttonptr;
|
|
|
|
static int button1justdown, button2justdown, button3justdown;
|
|
|
|
static float mouseX, mouseY;
|
|
|
|
|
|
|
|
static int menuOn;
|
|
|
|
static int menuInitialized;
|
|
|
|
static int screenWidth, screenHeight;
|
|
|
|
static RwRaster *cursor, *arrow;
|
|
|
|
|
|
|
|
static int firstBorder = 10;
|
|
|
|
static int topBorder = 40; //10;
|
|
|
|
static int leading = 4;
|
|
|
|
static int gap = 10;
|
|
|
|
static int minwidth = 100;
|
|
|
|
|
|
|
|
void drawMouse(void);
|
|
|
|
void drawArrow(RwRect r, int direction, int style);
|
|
|
|
|
|
|
|
Menu toplevel;
|
|
|
|
Menu *activeMenu = &toplevel;
|
|
|
|
Menu *deepestMenu = &toplevel;
|
|
|
|
Menu *mouseOverMenu;
|
|
|
|
MenuEntry *mouseOverEntry;
|
|
|
|
MenuEntry scrollUpEntry("SCROLLUP"), scrollDownEntry("SCROLLDOWN"); // dummies
|
|
|
|
|
|
|
|
|
|
|
|
#define KEYJUSTDOWN(k) ControlsManager.GetIsKeyboardKeyJustDown((RsKeyCodes)k)
|
|
|
|
#define KEYDOWN(k) ControlsManager.GetIsKeyboardKeyDown((RsKeyCodes)k)
|
|
|
|
#define CTRLJUSTDOWN(key) \
|
|
|
|
((KEYDOWN(rsLCTRL) || KEYDOWN(rsRCTRL)) && KEYJUSTDOWN((RsKeyCodes)key) || \
|
|
|
|
(KEYJUSTDOWN(rsLCTRL) || KEYJUSTDOWN(rsRCTRL)) && KEYDOWN((RsKeyCodes)key))
|
|
|
|
#define CTRLDOWN(key) ((KEYDOWN(rsLCTRL) || KEYDOWN(rsRCTRL)) && KEYDOWN((RsKeyCodes)key))
|
|
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
isMouseInRect(RwRect r)
|
|
|
|
{
|
|
|
|
return (mouseX >= r.x && mouseX < r.x+r.w &&
|
|
|
|
mouseY >= r.y && mouseY < r.y+r.h);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* MenuEntry
|
|
|
|
*/
|
|
|
|
|
|
|
|
MenuEntry::MenuEntry(const char *name)
|
|
|
|
{
|
|
|
|
this->type = MENUEMPTY;
|
|
|
|
this->name = strdup(name);
|
|
|
|
this->next = nil;
|
|
|
|
this->menu = nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
MenuEntry_Sub::MenuEntry_Sub(const char *name, Menu *menu)
|
|
|
|
: MenuEntry(name)
|
|
|
|
{
|
|
|
|
this->type = MENUSUB;
|
|
|
|
this->submenu = menu;
|
|
|
|
}
|
|
|
|
|
|
|
|
MenuEntry_Var::MenuEntry_Var(const char *name, int vartype)
|
|
|
|
: MenuEntry(name)
|
|
|
|
{
|
|
|
|
this->type = MENUVAR;
|
|
|
|
this->vartype = vartype;
|
|
|
|
this->maxvallen = 0;
|
|
|
|
this->wrapAround = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* *****************************
|
|
|
|
* MenuEntry_Int
|
|
|
|
* *****************************
|
|
|
|
*/
|
|
|
|
|
|
|
|
MenuEntry_Int::MenuEntry_Int(const char *name)
|
|
|
|
: MenuEntry_Var(name, MENUVAR_INT)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
#define X(NAME, TYPE, MAXLEN, FMT) \
|
|
|
|
int \
|
|
|
|
MenuEntry_##NAME::findStringLen(void){ \
|
|
|
|
TYPE i; \
|
|
|
|
int len, maxlen = 0; \
|
|
|
|
for(i = this->lowerBound; i <= this->upperBound; i++){ \
|
|
|
|
len = strlen(this->strings[i-this->lowerBound]); \
|
|
|
|
if(len > maxlen) \
|
|
|
|
maxlen = len; \
|
|
|
|
} \
|
|
|
|
return maxlen; \
|
|
|
|
} \
|
|
|
|
void \
|
|
|
|
MenuEntry_##NAME::processInput(bool mouseOver, bool selected) \
|
|
|
|
{ \
|
|
|
|
TYPE v, oldv; \
|
|
|
|
int overflow = 0; \
|
|
|
|
int underflow = 0; \
|
|
|
|
\
|
|
|
|
v = *this->variable; \
|
|
|
|
oldv = v; \
|
|
|
|
\
|
|
|
|
if((selected && leftjustdown) || (mouseOver && button3justdown)){ \
|
|
|
|
v -= this->step; \
|
|
|
|
if(v > oldv) \
|
|
|
|
underflow = 1; \
|
|
|
|
} \
|
|
|
|
if((selected && rightjustdown) || (mouseOver && button1justdown)){ \
|
|
|
|
v += this->step; \
|
|
|
|
if(v < oldv) \
|
|
|
|
overflow = 1; \
|
|
|
|
} \
|
|
|
|
if(this->wrapAround){ \
|
|
|
|
if(v > this->upperBound || overflow) v = this->lowerBound; \
|
|
|
|
if(v < this->lowerBound || underflow) v = this->upperBound; \
|
|
|
|
}else{ \
|
|
|
|
if(v > this->upperBound || overflow) v = this->upperBound; \
|
|
|
|
if(v < this->lowerBound || underflow) v = this->lowerBound; \
|
|
|
|
} \
|
|
|
|
\
|
|
|
|
*this->variable = v; \
|
|
|
|
if(oldv != v && this->triggerFunc) \
|
|
|
|
this->triggerFunc(); \
|
|
|
|
} \
|
|
|
|
void \
|
|
|
|
MenuEntry_##NAME::getValStr(char *str, int len) \
|
|
|
|
{ \
|
|
|
|
static char tmp[20]; \
|
|
|
|
if(this->strings){ \
|
|
|
|
snprintf(tmp, 20, "%%%ds", this->maxvallen); \
|
|
|
|
if(*this->variable < this->lowerBound || *this->variable > this->upperBound){ \
|
|
|
|
snprintf(str, len, "ERROR"); \
|
|
|
|
return; \
|
|
|
|
} \
|
|
|
|
snprintf(str, len, tmp, this->strings[*this->variable-this->lowerBound]); \
|
|
|
|
}else \
|
|
|
|
snprintf(str, len, this->fmt, *this->variable); \
|
|
|
|
} \
|
|
|
|
void \
|
|
|
|
MenuEntry_##NAME::setStrings(const char **strings) \
|
|
|
|
{ \
|
|
|
|
this->strings = strings; \
|
|
|
|
if(this->strings) \
|
|
|
|
this->maxvallen = findStringLen(); \
|
|
|
|
} \
|
|
|
|
MenuEntry_##NAME::MenuEntry_##NAME(const char *name, TYPE *ptr, TriggerFunc triggerFunc, TYPE step, TYPE lowerBound, TYPE upperBound, const char **strings) \
|
|
|
|
: MenuEntry_Int(name) \
|
|
|
|
{ \
|
|
|
|
this->variable = ptr; \
|
|
|
|
this->step = step; \
|
|
|
|
this->lowerBound = lowerBound; \
|
|
|
|
this->upperBound = upperBound; \
|
|
|
|
this->triggerFunc = triggerFunc; \
|
|
|
|
this->maxvallen = MAXLEN; \
|
|
|
|
this->fmt = FMT; \
|
|
|
|
this->setStrings(strings); \
|
|
|
|
}
|
|
|
|
INTTYPES
|
|
|
|
#undef X
|
|
|
|
|
|
|
|
/*
|
|
|
|
* *****************************
|
|
|
|
* MenuEntry_Float
|
|
|
|
* *****************************
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define X(NAME, TYPE, MAXLEN, FMT) \
|
|
|
|
MenuEntry_##NAME::MenuEntry_##NAME(const char *name, TYPE *ptr, TriggerFunc triggerFunc, TYPE step, TYPE lowerBound, TYPE upperBound) \
|
|
|
|
: MenuEntry_Var(name, MENUVAR_FLOAT) \
|
|
|
|
{ \
|
|
|
|
this->variable = ptr; \
|
|
|
|
this->step = step; \
|
|
|
|
this->lowerBound = lowerBound; \
|
|
|
|
this->upperBound = upperBound; \
|
|
|
|
this->triggerFunc = triggerFunc; \
|
|
|
|
this->maxvallen = MAXLEN; \
|
|
|
|
this->fmt = FMT; \
|
|
|
|
} \
|
|
|
|
void \
|
|
|
|
MenuEntry_##NAME::getValStr(char *str, int len) \
|
|
|
|
{ \
|
|
|
|
snprintf(str, len, this->fmt, *this->variable); \
|
|
|
|
} \
|
|
|
|
void \
|
|
|
|
MenuEntry_##NAME::processInput(bool mouseOver, bool selected) \
|
|
|
|
{ \
|
|
|
|
float v, oldv; \
|
|
|
|
int overflow = 0; \
|
|
|
|
int underflow = 0; \
|
|
|
|
\
|
|
|
|
v = *this->variable; \
|
|
|
|
oldv = v; \
|
|
|
|
\
|
|
|
|
if((selected && leftjustdown) || (mouseOver && button3justdown)){ \
|
|
|
|
v -= this->step; \
|
|
|
|
if(v > oldv) \
|
|
|
|
underflow = 1; \
|
|
|
|
} \
|
|
|
|
if((selected && rightjustdown) || (mouseOver && button1justdown)){ \
|
|
|
|
v += this->step; \
|
|
|
|
if(v < oldv) \
|
|
|
|
overflow = 1; \
|
|
|
|
} \
|
|
|
|
if(this->wrapAround){ \
|
|
|
|
if(v > this->upperBound || overflow) v = this->lowerBound; \
|
|
|
|
if(v < this->lowerBound || underflow) v = this->upperBound; \
|
|
|
|
}else{ \
|
|
|
|
if(v > this->upperBound || overflow) v = this->upperBound; \
|
|
|
|
if(v < this->lowerBound || underflow) v = this->lowerBound; \
|
|
|
|
} \
|
|
|
|
\
|
|
|
|
*this->variable = v; \
|
|
|
|
if(oldv != v && this->triggerFunc) \
|
|
|
|
this->triggerFunc(); \
|
|
|
|
}
|
|
|
|
|
|
|
|
FLOATTYPES
|
|
|
|
#undef X
|
|
|
|
|
|
|
|
/*
|
|
|
|
* *****************************
|
|
|
|
* MenuEntry_Cmd
|
|
|
|
* *****************************
|
|
|
|
*/
|
|
|
|
|
|
|
|
void
|
|
|
|
MenuEntry_Cmd::processInput(bool mouseOver, bool selected)
|
|
|
|
{
|
|
|
|
// Don't execute on button3
|
|
|
|
if(this->triggerFunc && (selected && (leftjustdown || rightjustdown) || (mouseOver && button1justdown)))
|
|
|
|
this->triggerFunc();
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
MenuEntry_Cmd::getValStr(char *str, int len)
|
|
|
|
{
|
|
|
|
strncpy(str, "<", len);
|
|
|
|
}
|
|
|
|
|
|
|
|
MenuEntry_Cmd::MenuEntry_Cmd(const char *name, TriggerFunc triggerFunc)
|
|
|
|
: MenuEntry_Var(name, MENUVAR_CMD)
|
|
|
|
{
|
|
|
|
this->maxvallen = 1;
|
|
|
|
this->triggerFunc = triggerFunc;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* *****************************
|
|
|
|
* Menu
|
|
|
|
* *****************************
|
|
|
|
*/
|
|
|
|
|
|
|
|
void
|
|
|
|
Menu::scroll(int off) {
|
|
|
|
if(isScrollingUp && off < 0)
|
|
|
|
scrollStart += off;
|
|
|
|
if(isScrollingDown && off > 0)
|
|
|
|
scrollStart += off;
|
|
|
|
if(scrollStart < 0) scrollStart = 0;
|
|
|
|
if(scrollStart > numEntries-numVisible) scrollStart = numEntries-numVisible;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
Menu::changeSelection(int newsel){
|
|
|
|
selection = newsel;
|
|
|
|
if(selection < 0) selection = 0;
|
|
|
|
if(selection >= numEntries) selection = numEntries-1;
|
|
|
|
if(selection < scrollStart) scrollStart = selection;
|
|
|
|
if(selection >= scrollStart+numVisible) scrollStart = selection-numVisible+1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
Menu::changeSelection(MenuEntry *sel)
|
|
|
|
{
|
|
|
|
MenuEntry *e;
|
|
|
|
int i = 0;
|
|
|
|
for(e = this->entries; e; e = e->next){
|
|
|
|
if(e == sel){
|
|
|
|
this->selection = i;
|
|
|
|
this->selectedEntry = sel;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
MenuEntry*
|
|
|
|
Menu::findEntry(const char *entryname)
|
|
|
|
{
|
|
|
|
MenuEntry *m;
|
|
|
|
for(m = this->entries; m; m = m->next)
|
|
|
|
if(strcmp(entryname, m->name) == 0)
|
|
|
|
return m;
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
Menu::insertEntrySorted(MenuEntry *entry)
|
|
|
|
{
|
|
|
|
MenuEntry **mp;
|
|
|
|
int cmp;
|
|
|
|
for(mp = &this->entries; *mp; mp = &(*mp)->next){
|
|
|
|
cmp = strcmp(entry->name, (*mp)->name);
|
|
|
|
if(cmp == 0)
|
|
|
|
return;
|
|
|
|
if(cmp < 0)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
entry->next = *mp;
|
|
|
|
*mp = entry;
|
|
|
|
entry->menu = this;
|
|
|
|
this->numEntries++;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
Menu::appendEntry(MenuEntry *entry)
|
|
|
|
{
|
|
|
|
MenuEntry **mp;
|
|
|
|
for(mp = &this->entries; *mp; mp = &(*mp)->next);
|
|
|
|
entry->next = *mp;
|
|
|
|
*mp = entry;
|
|
|
|
entry->menu = this;
|
|
|
|
this->numEntries++;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
Menu::update(void)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
int x, y;
|
|
|
|
Pt sz;
|
|
|
|
MenuEntry *e;
|
|
|
|
int onscreen;
|
|
|
|
x = this->r.x;
|
|
|
|
y = this->r.y + 18;
|
|
|
|
int end = this->r.y+this->r.h - 18;
|
|
|
|
this->numVisible = 0;
|
|
|
|
|
|
|
|
deepestMenu = this;
|
|
|
|
|
|
|
|
int bottomy = end;
|
|
|
|
onscreen = 1;
|
|
|
|
i = 0;
|
|
|
|
this->maxNameWidth = 0;
|
|
|
|
this->maxValWidth = 0;
|
|
|
|
this->isScrollingUp = this->scrollStart > 0;
|
|
|
|
this->isScrollingDown = false;
|
|
|
|
this->selectedEntry = nil;
|
|
|
|
for(e = this->entries; e; e = e->next){
|
|
|
|
sz = fontGetStringSize(e->name);
|
|
|
|
e->r.x = x;
|
|
|
|
e->r.y = y;
|
|
|
|
e->r.w = sz.x;
|
|
|
|
e->r.h = sz.y;
|
|
|
|
|
|
|
|
if(i == this->selection)
|
|
|
|
this->selectedEntry = e;
|
|
|
|
|
|
|
|
if(i >= this->scrollStart)
|
|
|
|
y += sz.y + leading*fontscale;
|
|
|
|
if(y >= end){
|
|
|
|
this->isScrollingDown = true;
|
|
|
|
onscreen = 0;
|
|
|
|
}else
|
|
|
|
bottomy = y;
|
|
|
|
if(i >= this->scrollStart && onscreen)
|
|
|
|
this->numVisible++;
|
|
|
|
|
|
|
|
if(e->type == MENUVAR){
|
|
|
|
int valwidth = fontGetLen(((MenuEntry_Var*)e)->getValWidth());
|
|
|
|
if(valwidth > maxValWidth)
|
|
|
|
maxValWidth = valwidth;
|
|
|
|
}
|
|
|
|
if(e->r.w > maxNameWidth)
|
|
|
|
maxNameWidth = e->r.w;
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
if(this->r.w < maxNameWidth + maxValWidth + gap*fontscale)
|
|
|
|
this->r.w = maxNameWidth + maxValWidth + gap*fontscale;
|
|
|
|
|
|
|
|
this->scrollUpR = this->r;
|
|
|
|
this->scrollUpR.h = 16;
|
|
|
|
this->scrollDownR = this->scrollUpR;
|
|
|
|
this->scrollDownR.y = bottomy;
|
|
|
|
|
|
|
|
// Update active submenu
|
|
|
|
if(this->selectedEntry && this->selectedEntry->type == MENUSUB){
|
|
|
|
Menu *submenu = ((MenuEntry_Sub*)this->selectedEntry)->submenu;
|
|
|
|
submenu->r.x = this->r.x+this->r.w + 10;
|
|
|
|
submenu->r.y = this->r.y;
|
|
|
|
submenu->r.w = minwidth; // update menu will expand
|
|
|
|
submenu->r.h = this->r.h;
|
|
|
|
submenu->update();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
Menu::draw(void)
|
|
|
|
{
|
|
|
|
static char val[100];
|
|
|
|
int i;
|
|
|
|
MenuEntry *e;
|
|
|
|
i = 0;
|
|
|
|
for(e = this->entries; e; e = e->next){
|
|
|
|
if(i >= this->scrollStart+this->numVisible)
|
|
|
|
break;
|
|
|
|
if(i >= this->scrollStart){
|
|
|
|
int style = MENUFONT_NORMAL;
|
|
|
|
if(i == this->selection)
|
|
|
|
style = this == activeMenu ? MENUFONT_SEL_ACTIVE : MENUFONT_SEL_INACTIVE;
|
|
|
|
if(style != MENUFONT_SEL_ACTIVE && e == mouseOverEntry)
|
|
|
|
style = MENUFONT_MOUSE;
|
|
|
|
fontPrint(e->name, e->r.x, e->r.y, style);
|
|
|
|
if(e->type == MENUVAR){
|
|
|
|
int valw = fontGetLen(((MenuEntry_Var*)e)->getValWidth());
|
|
|
|
((MenuEntry_Var*)e)->getValStr(val, 100);
|
|
|
|
fontPrint(val, e->r.x+this->r.w-valw, e->r.y, style);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(this->isScrollingUp)
|
|
|
|
drawArrow(this->scrollUpR, -1, isMouseInRect(this->scrollUpR));
|
|
|
|
if(this->isScrollingDown)
|
|
|
|
drawArrow(this->scrollDownR, 1, isMouseInRect(this->scrollDownR));
|
|
|
|
|
|
|
|
if(this->selectedEntry && this->selectedEntry->type == MENUSUB)
|
|
|
|
((MenuEntry_Sub*)this->selectedEntry)->submenu->draw();
|
|
|
|
}
|
|
|
|
|
2020-05-01 11:31:29 -04:00
|
|
|
Menu::~Menu(void)
|
|
|
|
{
|
|
|
|
MenuEntry *e, *next;
|
|
|
|
for(e = entries; e; e = next){
|
|
|
|
next = e->next;
|
|
|
|
delete e;
|
|
|
|
}
|
|
|
|
memset(this, 0, sizeof(Menu));
|
|
|
|
}
|
|
|
|
|
2020-04-24 07:27:02 -04:00
|
|
|
Menu*
|
|
|
|
findMenu(const char *name)
|
|
|
|
{
|
|
|
|
Menu *m;
|
|
|
|
MenuEntry *e;
|
|
|
|
char *tmppath = strdup(name);
|
|
|
|
char *next, *curname;
|
|
|
|
|
|
|
|
curname = tmppath;
|
|
|
|
next = curname;
|
|
|
|
|
|
|
|
m = &toplevel;
|
|
|
|
while(*next){
|
|
|
|
curname = next;
|
|
|
|
while(*next){
|
|
|
|
if(*next == '|'){
|
|
|
|
*next++ = '\0';
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
next++;
|
|
|
|
}
|
|
|
|
e = m->findEntry(curname);
|
|
|
|
if(e){
|
|
|
|
// return an error if the entry exists but isn't a menu
|
|
|
|
if(e->type != MENUSUB){
|
|
|
|
free(tmppath);
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
m = ((MenuEntry_Sub*)e)->submenu;
|
|
|
|
}else{
|
|
|
|
// Create submenus that don't exist yet
|
|
|
|
Menu *submenu = new Menu();
|
|
|
|
submenu->parent = m;
|
|
|
|
MenuEntry *me = new MenuEntry_Sub(curname, submenu);
|
|
|
|
// Don't sort submenus outside the toplevel menu
|
|
|
|
if(m == &toplevel)
|
|
|
|
m->insertEntrySorted(me);
|
|
|
|
else
|
|
|
|
m->appendEntry(me);
|
|
|
|
m = submenu;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
free(tmppath);
|
|
|
|
return m;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* ****************
|
|
|
|
* debug menu
|
|
|
|
* ****************
|
|
|
|
*/
|
|
|
|
|
|
|
|
static uint8 cursorPx[] = {
|
|
|
|
#include "cursor.inc"
|
|
|
|
};
|
|
|
|
|
|
|
|
static uint8 arrowPx[] = {
|
|
|
|
#include "arrow.inc"
|
|
|
|
};
|
|
|
|
|
|
|
|
void
|
2020-04-24 18:40:14 -04:00
|
|
|
DebugMenuInit(void)
|
2020-04-24 07:27:02 -04:00
|
|
|
{
|
|
|
|
createMenuFont();
|
|
|
|
|
|
|
|
RwInt32 w, h, d, flags;
|
|
|
|
RwImage *img = RwImageCreate(16, 16, 32);
|
|
|
|
assert(img);
|
|
|
|
RwImageSetPixels(img, cursorPx);
|
|
|
|
RwImageSetStride(img, RwImageGetWidth(img)*4);
|
|
|
|
RwImageFindRasterFormat(img, rwRASTERTYPETEXTURE, &w, &h, &d, &flags);
|
|
|
|
cursor = RwRasterCreate(w, h, d, flags);
|
|
|
|
cursor = RwRasterSetFromImage(cursor, img);
|
|
|
|
assert(cursor);
|
|
|
|
RwImageDestroy(img);
|
|
|
|
|
|
|
|
img = RwImageCreate(32, 16, 32);
|
|
|
|
assert(img);
|
|
|
|
RwImageSetPixels(img, arrowPx);
|
|
|
|
RwImageSetStride(img, RwImageGetWidth(img)*4);
|
|
|
|
RwImageFindRasterFormat(img, rwRASTERTYPETEXTURE, &w, &h, &d, &flags);
|
|
|
|
arrow = RwRasterCreate(w, h, d, flags);
|
|
|
|
arrow = RwRasterSetFromImage(arrow, img);
|
|
|
|
assert(arrow);
|
|
|
|
RwImageDestroy(img);
|
2020-04-24 18:40:14 -04:00
|
|
|
|
2020-05-01 11:31:29 -04:00
|
|
|
|
2020-04-24 18:40:14 -04:00
|
|
|
menuInitialized = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
DebugMenuShutdown(void)
|
|
|
|
{
|
|
|
|
if(menuInitialized){
|
|
|
|
destroyMenuFont();
|
|
|
|
RwRasterDestroy(cursor);
|
|
|
|
cursor = nil;
|
|
|
|
RwRasterDestroy(arrow);
|
|
|
|
arrow = nil;
|
2020-05-01 11:31:29 -04:00
|
|
|
|
|
|
|
toplevel.~Menu();
|
|
|
|
new (&toplevel) Menu();
|
|
|
|
|
|
|
|
activeMenu = &toplevel;
|
|
|
|
deepestMenu = &toplevel;
|
|
|
|
mouseOverMenu = nil;
|
|
|
|
mouseOverEntry = nil;
|
2020-04-24 18:40:14 -04:00
|
|
|
}
|
|
|
|
menuInitialized = false;
|
2020-04-24 07:27:02 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
processInput(void)
|
|
|
|
{
|
|
|
|
int shift = KEYDOWN(rsRSHIFT) || KEYDOWN(rsLSHIFT);
|
|
|
|
#define X(var, keycode) var = KEYJUSTDOWN(keycode);
|
|
|
|
MUHKEYS
|
|
|
|
#undef X
|
|
|
|
|
|
|
|
// Implement auto-repeat
|
|
|
|
#define X(var, keycode) \
|
|
|
|
if(var){ \
|
|
|
|
repeattime = downtime = CTimer::GetTimeInMilliseconds(); \
|
|
|
|
lastkeydown = keycode; \
|
|
|
|
keyptr = &var; \
|
|
|
|
}
|
|
|
|
MUHKEYS
|
|
|
|
#undef X
|
|
|
|
if(lastkeydown){
|
|
|
|
if(KEYDOWN(lastkeydown)){
|
|
|
|
int curtime = CTimer::GetTimeInMilliseconds();
|
|
|
|
if(curtime - downtime > REPEATDELAY){
|
|
|
|
if(curtime - repeattime > REPEATINTERVAL){
|
|
|
|
repeattime = curtime;
|
|
|
|
*keyptr = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}else{
|
|
|
|
lastkeydown = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Also for mouse buttons
|
|
|
|
#define X(var, num) \
|
|
|
|
if(var){ \
|
|
|
|
repeattime = downtime = CTimer::GetTimeInMilliseconds(); \
|
|
|
|
lastbuttondown = num; \
|
|
|
|
buttonptr = &var; \
|
|
|
|
}
|
|
|
|
MUHBUTTONS
|
|
|
|
#undef X
|
|
|
|
if(lastbuttondown){
|
|
|
|
if(buttondown[lastbuttondown-1]){
|
|
|
|
int curtime = CTimer::GetTimeInMilliseconds();
|
|
|
|
if(curtime - downtime > REPEATDELAY){
|
|
|
|
if(curtime - repeattime > REPEATINTERVAL){
|
|
|
|
repeattime = curtime;
|
|
|
|
*buttonptr = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}else{
|
|
|
|
lastbuttondown = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Walk through all visible menus and figure out which one the mouse is over
|
|
|
|
mouseOverMenu = nil;
|
|
|
|
mouseOverEntry = nil;
|
|
|
|
Menu *menu;
|
|
|
|
for(menu = deepestMenu; menu; menu = menu->parent)
|
|
|
|
if(isMouseInRect(menu->r)){
|
|
|
|
mouseOverMenu = menu;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if(mouseOverMenu){
|
|
|
|
// Walk all visibile entries and figure out which one the mouse is over
|
|
|
|
MenuEntry *e;
|
|
|
|
int i = 0;
|
|
|
|
for(e = mouseOverMenu->entries; e; e = e->next){
|
|
|
|
if(i >= mouseOverMenu->scrollStart+mouseOverMenu->numVisible)
|
|
|
|
break;
|
|
|
|
if(i >= mouseOverMenu->scrollStart){
|
|
|
|
RwRect r = e->r;
|
|
|
|
r.w = mouseOverMenu->r.w; // span the whole menu
|
|
|
|
if(isMouseInRect(r)){
|
|
|
|
mouseOverEntry = e;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
if(mouseOverMenu->isScrollingUp && isMouseInRect(mouseOverMenu->scrollUpR)){
|
|
|
|
mouseOverEntry = &scrollUpEntry;
|
|
|
|
mouseOverEntry->r = mouseOverMenu->scrollUpR;
|
|
|
|
mouseOverEntry->menu = mouseOverMenu;
|
|
|
|
mouseOverEntry->type = MENUSCROLL;
|
|
|
|
}
|
|
|
|
if(mouseOverMenu->isScrollingDown && isMouseInRect(mouseOverMenu->scrollDownR)){
|
|
|
|
mouseOverEntry = &scrollDownEntry;
|
|
|
|
mouseOverEntry->r = mouseOverMenu->scrollDownR;
|
|
|
|
mouseOverEntry->menu = mouseOverMenu;
|
|
|
|
mouseOverEntry->type = MENUSCROLL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if(pgupjustdown)
|
|
|
|
activeMenu->scroll(shift ? -5 : -1);
|
|
|
|
if(pgdnjustdown)
|
|
|
|
activeMenu->scroll(shift ? 5 : 1);
|
|
|
|
if(downjustdown)
|
|
|
|
activeMenu->changeSelection(activeMenu->selection + (shift ? 5 : 1));
|
|
|
|
if(upjustdown)
|
|
|
|
activeMenu->changeSelection(activeMenu->selection - (shift ? 5 : 1));
|
|
|
|
|
|
|
|
if(CPad::NewMouseControllerState.WHEELUP){
|
|
|
|
if(mouseOverMenu)
|
|
|
|
activeMenu = mouseOverMenu;
|
|
|
|
activeMenu->scroll(shift ? -5 : -1);
|
|
|
|
}
|
|
|
|
if(CPad::NewMouseControllerState.WHEELDN){
|
|
|
|
if(mouseOverMenu)
|
|
|
|
activeMenu = mouseOverMenu;
|
|
|
|
activeMenu->scroll(shift ? 5 : 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(mouseOverEntry == &scrollUpEntry){
|
|
|
|
if(button1justdown){
|
|
|
|
activeMenu = mouseOverEntry->menu;
|
|
|
|
activeMenu->scroll(shift ? -5 : -1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(mouseOverEntry == &scrollDownEntry){
|
|
|
|
if(button1justdown){
|
|
|
|
activeMenu = mouseOverEntry->menu;
|
|
|
|
activeMenu->scroll(shift ? 5 : 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Have to call this before processInput below because menu entry can change
|
|
|
|
if((button1justdown || button3justdown) && mouseOverEntry){
|
|
|
|
activeMenu = mouseOverEntry->menu;
|
|
|
|
activeMenu->changeSelection(mouseOverEntry);
|
|
|
|
}
|
|
|
|
if(KEYJUSTDOWN(rsENTER)){
|
|
|
|
if(activeMenu->selectedEntry && activeMenu->selectedEntry->type == MENUSUB)
|
|
|
|
activeMenu = ((MenuEntry_Sub*)activeMenu->selectedEntry)->submenu;
|
|
|
|
}else if(KEYJUSTDOWN(rsBACKSP)){
|
|
|
|
if(activeMenu->parent)
|
|
|
|
activeMenu = activeMenu->parent;
|
|
|
|
}else{
|
|
|
|
if(mouseOverEntry && mouseOverEntry->type == MENUVAR)
|
|
|
|
((MenuEntry_Var*)mouseOverEntry)->processInput(true, mouseOverEntry == activeMenu->selectedEntry);
|
|
|
|
if(activeMenu->selectedEntry && activeMenu->selectedEntry->type == MENUVAR &&
|
|
|
|
mouseOverEntry != activeMenu->selectedEntry)
|
|
|
|
((MenuEntry_Var*)activeMenu->selectedEntry)->processInput(false, true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
updateMouse(void)
|
|
|
|
{
|
|
|
|
CPad *pad = CPad::GetPad(0);
|
|
|
|
int dirX = 1;
|
|
|
|
int dirY = 1;
|
|
|
|
|
|
|
|
if(MousePointerStateHelper.bInvertHorizontally) dirX = -1;
|
|
|
|
if(MousePointerStateHelper.bInvertVertically) dirY = -1;
|
|
|
|
|
|
|
|
mouseX += pad->NewMouseControllerState.x*dirX;
|
|
|
|
mouseY += pad->NewMouseControllerState.y*dirY;
|
|
|
|
|
|
|
|
if(mouseX < 0.0f) mouseX = 0.0f;
|
|
|
|
if(mouseY < 0.0f) mouseY = 0.0f;
|
|
|
|
if(mouseX >= screenWidth) mouseX = screenWidth;
|
|
|
|
if(mouseY >= screenHeight) mouseY = screenHeight;
|
|
|
|
|
|
|
|
button1justdown = pad->NewMouseControllerState.LMB && !pad->OldMouseControllerState.LMB;
|
|
|
|
button2justdown = pad->NewMouseControllerState.MMB && !pad->OldMouseControllerState.MMB;
|
|
|
|
button3justdown = pad->NewMouseControllerState.RMB && !pad->OldMouseControllerState.RMB;
|
|
|
|
buttondown[0] = pad->NewMouseControllerState.LMB;
|
|
|
|
buttondown[1] = pad->NewMouseControllerState.MMB;
|
|
|
|
buttondown[2] = pad->NewMouseControllerState.RMB;
|
|
|
|
|
|
|
|
// Zero the mouse position so the camera won't move
|
|
|
|
pad->NewMouseControllerState.x = 0.0f;
|
|
|
|
pad->NewMouseControllerState.y = 0.0f;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
DebugMenuProcess(void)
|
|
|
|
{
|
|
|
|
// We only process some input here
|
|
|
|
|
|
|
|
CPad *pad = CPad::GetPad(0);
|
|
|
|
if(CTRLJUSTDOWN('M'))
|
|
|
|
menuOn = !menuOn;
|
|
|
|
if(!menuOn)
|
|
|
|
return;
|
|
|
|
|
|
|
|
pad->DisablePlayerControls = 1;
|
|
|
|
// TODO: this could happen earlier
|
2020-04-24 18:40:14 -04:00
|
|
|
if(!menuInitialized)
|
|
|
|
DebugMenuInit();
|
2020-04-24 07:27:02 -04:00
|
|
|
updateMouse();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef LIBRW
|
|
|
|
#define CURRENTCAM (rw::engine->currentCamera)
|
|
|
|
#else
|
|
|
|
#define CURRENTCAM ((RwCamera*)RWSRCGLOBAL(curCamera))
|
|
|
|
#endif
|
|
|
|
|
|
|
|
void
|
|
|
|
DebugMenuRender(void)
|
|
|
|
{
|
|
|
|
if(!menuOn)
|
|
|
|
return;
|
|
|
|
|
|
|
|
RwRenderStateSet(rwRENDERSTATEZTESTENABLE, 0);
|
|
|
|
RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, 0);
|
|
|
|
RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE);
|
|
|
|
RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA);
|
|
|
|
RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA);
|
|
|
|
RwRenderStateSet(rwRENDERSTATEFOGENABLE, 0);
|
|
|
|
RwRenderStateSet(rwRENDERSTATECULLMODE, (void*)rwCULLMODECULLNONE);
|
|
|
|
|
|
|
|
RwCamera *cam = CURRENTCAM;
|
|
|
|
screenWidth = RwRasterGetWidth(RwCameraGetRaster(cam));
|
|
|
|
screenHeight = RwRasterGetHeight(RwCameraGetRaster(cam));
|
|
|
|
|
|
|
|
// if(screenHeight > 1080)
|
|
|
|
// fontscale = 2;
|
|
|
|
// else
|
|
|
|
fontscale = 1;
|
|
|
|
|
|
|
|
Pt sz;
|
|
|
|
sz = fontPrint("Debug Menu", firstBorder*fontscale+30, topBorder, 0);
|
|
|
|
|
|
|
|
toplevel.r.x = firstBorder*fontscale;
|
|
|
|
toplevel.r.y = topBorder + sz.y + 10;
|
|
|
|
toplevel.r.w = minwidth; // update menu will expand
|
|
|
|
toplevel.r.h = screenHeight - 10 - toplevel.r.y;
|
|
|
|
toplevel.update();
|
|
|
|
toplevel.draw();
|
|
|
|
processInput();
|
|
|
|
RtCharsetBufferFlush();
|
|
|
|
|
|
|
|
drawMouse();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
drawArrow(RwRect r, int direction, int style)
|
|
|
|
{
|
|
|
|
static RwImVertexIndex indices[] = { 0, 1, 2, 2, 1, 3 };
|
|
|
|
static RwIm2DVertex arrowVerts[4];
|
|
|
|
|
|
|
|
RwCamera *cam = CURRENTCAM;
|
|
|
|
float recipz = 1.0f/RwCameraGetNearClipPlane(cam);
|
|
|
|
|
|
|
|
int width = RwRasterGetWidth(arrow);
|
|
|
|
int height = RwRasterGetHeight(arrow);
|
|
|
|
|
|
|
|
int left = r.x + (r.w - width)/2;
|
|
|
|
int right = left + width;
|
|
|
|
int top = r.y;
|
|
|
|
int bottom = r.y+r.h;
|
|
|
|
|
2020-04-26 15:50:52 -04:00
|
|
|
float umin = HALFPX / width;
|
|
|
|
float vmin = HALFPX / height;
|
|
|
|
float umax = (width + HALFPX) / width;
|
|
|
|
float vmax = (height + HALFPX) / height;
|
2020-04-24 07:27:02 -04:00
|
|
|
if(direction < 0){
|
2020-04-26 15:50:52 -04:00
|
|
|
vmin = (height - HALFPX) / height;
|
|
|
|
vmax = -HALFPX / height;
|
2020-04-24 07:27:02 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
if(style){
|
|
|
|
RwIm2DVertexSetScreenX(&arrowVerts[0], r.x);
|
|
|
|
RwIm2DVertexSetScreenY(&arrowVerts[0], r.y-1);
|
|
|
|
RwIm2DVertexSetScreenZ(&arrowVerts[0], RwIm2DGetNearScreenZ());
|
|
|
|
RwIm2DVertexSetCameraZ(&arrowVerts[0], RwCameraGetNearClipPlane(cam));
|
|
|
|
RwIm2DVertexSetRecipCameraZ(&arrowVerts[0], recipz);
|
|
|
|
RwIm2DVertexSetIntRGBA(&arrowVerts[0], 132, 132, 132, 255);
|
|
|
|
|
|
|
|
RwIm2DVertexSetScreenX(&arrowVerts[1], r.x+r.w);
|
|
|
|
RwIm2DVertexSetScreenY(&arrowVerts[1], r.y-1);
|
|
|
|
RwIm2DVertexSetScreenZ(&arrowVerts[1], RwIm2DGetNearScreenZ());
|
|
|
|
RwIm2DVertexSetCameraZ(&arrowVerts[1], RwCameraGetNearClipPlane(cam));
|
|
|
|
RwIm2DVertexSetRecipCameraZ(&arrowVerts[1], recipz);
|
|
|
|
RwIm2DVertexSetIntRGBA(&arrowVerts[1], 132, 132, 132, 255);
|
|
|
|
|
|
|
|
RwIm2DVertexSetScreenX(&arrowVerts[2], r.x);
|
|
|
|
RwIm2DVertexSetScreenY(&arrowVerts[2], r.y+r.h+1);
|
|
|
|
RwIm2DVertexSetScreenZ(&arrowVerts[2], RwIm2DGetNearScreenZ());
|
|
|
|
RwIm2DVertexSetCameraZ(&arrowVerts[2], RwCameraGetNearClipPlane(cam));
|
|
|
|
RwIm2DVertexSetRecipCameraZ(&arrowVerts[2], recipz);
|
|
|
|
RwIm2DVertexSetIntRGBA(&arrowVerts[2], 132, 132, 132, 255);
|
|
|
|
|
|
|
|
RwIm2DVertexSetScreenX(&arrowVerts[3], r.x+r.w);
|
|
|
|
RwIm2DVertexSetScreenY(&arrowVerts[3], r.y+r.h+1);
|
|
|
|
RwIm2DVertexSetScreenZ(&arrowVerts[3], RwIm2DGetNearScreenZ());
|
|
|
|
RwIm2DVertexSetCameraZ(&arrowVerts[3], RwCameraGetNearClipPlane(cam));
|
|
|
|
RwIm2DVertexSetRecipCameraZ(&arrowVerts[3], recipz);
|
|
|
|
RwIm2DVertexSetIntRGBA(&arrowVerts[3], 132, 132, 132, 255);
|
|
|
|
|
|
|
|
RwRenderStateSet(rwRENDERSTATETEXTURERASTER, nil);
|
|
|
|
RwRenderStateSet(rwRENDERSTATETEXTUREFILTER, (void*)rwFILTERLINEAR);
|
|
|
|
RwIm2DRenderIndexedPrimitive(rwPRIMTYPETRILIST, arrowVerts, 4, indices, 6);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
RwIm2DVertexSetScreenX(&arrowVerts[0], left);
|
|
|
|
RwIm2DVertexSetScreenY(&arrowVerts[0], top);
|
|
|
|
RwIm2DVertexSetScreenZ(&arrowVerts[0], RwIm2DGetNearScreenZ());
|
|
|
|
RwIm2DVertexSetCameraZ(&arrowVerts[0], RwCameraGetNearClipPlane(cam));
|
|
|
|
RwIm2DVertexSetRecipCameraZ(&arrowVerts[0], recipz);
|
|
|
|
RwIm2DVertexSetIntRGBA(&arrowVerts[0], 255, 255, 255, 255);
|
|
|
|
RwIm2DVertexSetU(&arrowVerts[0], umin, recipz);
|
|
|
|
RwIm2DVertexSetV(&arrowVerts[0], vmin, recipz);
|
|
|
|
|
|
|
|
RwIm2DVertexSetScreenX(&arrowVerts[1], right);
|
|
|
|
RwIm2DVertexSetScreenY(&arrowVerts[1], top);
|
|
|
|
RwIm2DVertexSetScreenZ(&arrowVerts[1], RwIm2DGetNearScreenZ());
|
|
|
|
RwIm2DVertexSetCameraZ(&arrowVerts[1], RwCameraGetNearClipPlane(cam));
|
|
|
|
RwIm2DVertexSetRecipCameraZ(&arrowVerts[1], recipz);
|
|
|
|
RwIm2DVertexSetIntRGBA(&arrowVerts[1], 255, 255, 255, 255);
|
|
|
|
RwIm2DVertexSetU(&arrowVerts[1], umax, recipz);
|
|
|
|
RwIm2DVertexSetV(&arrowVerts[1], vmin, recipz);
|
|
|
|
|
|
|
|
RwIm2DVertexSetScreenX(&arrowVerts[2], left);
|
|
|
|
RwIm2DVertexSetScreenY(&arrowVerts[2], bottom);
|
|
|
|
RwIm2DVertexSetScreenZ(&arrowVerts[2], RwIm2DGetNearScreenZ());
|
|
|
|
RwIm2DVertexSetCameraZ(&arrowVerts[2], RwCameraGetNearClipPlane(cam));
|
|
|
|
RwIm2DVertexSetRecipCameraZ(&arrowVerts[2], recipz);
|
|
|
|
RwIm2DVertexSetIntRGBA(&arrowVerts[2], 255, 255, 255, 255);
|
|
|
|
RwIm2DVertexSetU(&arrowVerts[2], umin, recipz);
|
|
|
|
RwIm2DVertexSetV(&arrowVerts[2], vmax, recipz);
|
|
|
|
|
|
|
|
RwIm2DVertexSetScreenX(&arrowVerts[3], right);
|
|
|
|
RwIm2DVertexSetScreenY(&arrowVerts[3], bottom);
|
|
|
|
RwIm2DVertexSetScreenZ(&arrowVerts[3], RwIm2DGetNearScreenZ());
|
|
|
|
RwIm2DVertexSetCameraZ(&arrowVerts[3], RwCameraGetNearClipPlane(cam));
|
|
|
|
RwIm2DVertexSetRecipCameraZ(&arrowVerts[3], recipz);
|
|
|
|
RwIm2DVertexSetIntRGBA(&arrowVerts[3], 255, 255, 255, 255);
|
|
|
|
RwIm2DVertexSetU(&arrowVerts[3], umax, recipz);
|
|
|
|
RwIm2DVertexSetV(&arrowVerts[3], vmax, recipz);
|
|
|
|
|
|
|
|
RwRenderStateSet(rwRENDERSTATETEXTURERASTER, arrow);
|
|
|
|
RwRenderStateSet(rwRENDERSTATETEXTUREFILTER, (void*)rwFILTERLINEAR);
|
|
|
|
RwIm2DRenderIndexedPrimitive(rwPRIMTYPETRILIST, arrowVerts, 4, indices, 6);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
drawMouse(void)
|
|
|
|
{
|
|
|
|
static RwImVertexIndex indices[] = { 0, 1, 2, 2, 1, 3 };
|
|
|
|
static RwIm2DVertex vertices[4];
|
|
|
|
RwIm2DVertex *vert;
|
|
|
|
RwCamera *cam;
|
|
|
|
cam = CURRENTCAM;
|
|
|
|
float x = mouseX;
|
|
|
|
float y = mouseY;
|
|
|
|
float w = RwRasterGetWidth(cursor);
|
|
|
|
float h = RwRasterGetHeight(cursor);
|
|
|
|
float recipz = 1.0f/RwCameraGetNearClipPlane(cam);
|
|
|
|
|
2020-04-26 15:50:52 -04:00
|
|
|
float umin = HALFPX / w;
|
|
|
|
float vmin = HALFPX / h;
|
|
|
|
float umax = (w + HALFPX) / w;
|
|
|
|
float vmax = (h + HALFPX) / h;
|
2020-04-24 07:27:02 -04:00
|
|
|
|
|
|
|
vert = vertices;
|
|
|
|
RwIm2DVertexSetScreenX(vert, x);
|
|
|
|
RwIm2DVertexSetScreenY(vert, y);
|
|
|
|
RwIm2DVertexSetScreenZ(vert, RwIm2DGetNearScreenZ());
|
|
|
|
RwIm2DVertexSetCameraZ(vert, RwCameraGetNearClipPlane(cam));
|
|
|
|
RwIm2DVertexSetRecipCameraZ(vert, recipz);
|
|
|
|
RwIm2DVertexSetIntRGBA(vert, 255, 255, 255, 255);
|
|
|
|
RwIm2DVertexSetU(vert, umin, recipz);
|
|
|
|
RwIm2DVertexSetV(vert, vmin, recipz);
|
|
|
|
vert++;
|
|
|
|
|
|
|
|
RwIm2DVertexSetScreenX(vert, x+w);
|
|
|
|
RwIm2DVertexSetScreenY(vert, y);
|
|
|
|
RwIm2DVertexSetScreenZ(vert, RwIm2DGetNearScreenZ());
|
|
|
|
RwIm2DVertexSetCameraZ(vert, RwCameraGetNearClipPlane(cam));
|
|
|
|
RwIm2DVertexSetRecipCameraZ(vert, recipz);
|
|
|
|
RwIm2DVertexSetIntRGBA(vert, 255, 255, 255, 255);
|
|
|
|
RwIm2DVertexSetU(vert, umax, recipz);
|
|
|
|
RwIm2DVertexSetV(vert, vmin, recipz);
|
|
|
|
vert++;
|
|
|
|
|
|
|
|
RwIm2DVertexSetScreenX(vert, x);
|
|
|
|
RwIm2DVertexSetScreenY(vert, y+h);
|
|
|
|
RwIm2DVertexSetScreenZ(vert, RwIm2DGetNearScreenZ());
|
|
|
|
RwIm2DVertexSetCameraZ(vert, RwCameraGetNearClipPlane(cam));
|
|
|
|
RwIm2DVertexSetRecipCameraZ(vert, recipz);
|
|
|
|
RwIm2DVertexSetIntRGBA(vert, 255, 255, 255, 255);
|
|
|
|
RwIm2DVertexSetU(vert, umin, recipz);
|
|
|
|
RwIm2DVertexSetV(vert, vmax, recipz);
|
|
|
|
vert++;
|
|
|
|
|
|
|
|
RwIm2DVertexSetScreenX(vert, x+w);
|
|
|
|
RwIm2DVertexSetScreenY(vert, y+h);
|
|
|
|
RwIm2DVertexSetScreenZ(vert, RwIm2DGetNearScreenZ());
|
|
|
|
RwIm2DVertexSetCameraZ(vert, RwCameraGetNearClipPlane(cam));
|
|
|
|
RwIm2DVertexSetRecipCameraZ(vert, recipz);
|
|
|
|
RwIm2DVertexSetIntRGBA(vert, 255, 255, 255, 255);
|
|
|
|
RwIm2DVertexSetU(vert, umax, recipz);
|
|
|
|
RwIm2DVertexSetV(vert, vmax, recipz);
|
|
|
|
vert++;
|
|
|
|
|
|
|
|
RwRenderStateSet(rwRENDERSTATETEXTURERASTER, cursor);
|
|
|
|
RwRenderStateSet(rwRENDERSTATETEXTUREFILTER, (void*)rwFILTERLINEAR);
|
|
|
|
RwIm2DRenderIndexedPrimitive(rwPRIMTYPETRILIST, vertices, 4, indices, 6);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Generate interfaces
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
#define X(NAME, TYPE, unused1, unused2) \
|
|
|
|
MenuEntry* \
|
|
|
|
DebugMenuAdd##NAME(const char *path, const char *name, TYPE *ptr, TriggerFunc triggerFunc, TYPE step, TYPE lowerBound, TYPE upperBound, const char **strings) \
|
|
|
|
{ \
|
|
|
|
Menu *m = findMenu(path); \
|
|
|
|
if(m == nil) \
|
|
|
|
return nil; \
|
|
|
|
MenuEntry *e = new MenuEntry_##NAME(name, ptr, triggerFunc, step, lowerBound, upperBound, strings); \
|
|
|
|
m->appendEntry(e); \
|
|
|
|
return e; \
|
|
|
|
}
|
|
|
|
INTTYPES
|
|
|
|
#undef X
|
|
|
|
|
|
|
|
#define X(NAME, TYPE, unused1, unused2) \
|
|
|
|
MenuEntry* \
|
|
|
|
DebugMenuAdd##NAME(const char *path, const char *name, TYPE *ptr, TriggerFunc triggerFunc, TYPE step, TYPE lowerBound, TYPE upperBound) \
|
|
|
|
{ \
|
|
|
|
Menu *m = findMenu(path); \
|
|
|
|
if(m == nil) \
|
|
|
|
return nil; \
|
|
|
|
MenuEntry *e = new MenuEntry_##NAME(name, ptr, triggerFunc, step, lowerBound, upperBound); \
|
|
|
|
m->appendEntry(e); \
|
|
|
|
return e; \
|
|
|
|
}
|
|
|
|
FLOATTYPES
|
|
|
|
#undef X
|
|
|
|
|
|
|
|
MenuEntry* \
|
|
|
|
DebugMenuAddCmd(const char *path, const char *name, TriggerFunc triggerFunc)
|
|
|
|
{
|
|
|
|
Menu *m = findMenu(path);
|
|
|
|
if(m == nil)
|
|
|
|
return nil;
|
|
|
|
MenuEntry *e = new MenuEntry_Cmd(name, triggerFunc);
|
|
|
|
m->appendEntry(e);
|
|
|
|
return e;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
DebugMenuEntrySetWrap(MenuEntry *e, bool wrap)
|
|
|
|
{
|
|
|
|
if(e && e->type == MENUVAR)
|
|
|
|
((MenuEntry_Var*)e)->wrapAround = wrap;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
DebugMenuEntrySetStrings(MenuEntry *e, const char **strings)
|
|
|
|
{
|
|
|
|
if(e && e->type == MENUVAR_INT)
|
|
|
|
((MenuEntry_Int*)e)->setStrings(strings);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
DebugMenuEntrySetAddress(MenuEntry *e, void *addr)
|
|
|
|
{
|
|
|
|
if(e && e->type == MENUVAR){
|
|
|
|
MenuEntry_Var *ev = (MenuEntry_Var*)e;
|
|
|
|
// HACK - we know the variable field is at the same address
|
|
|
|
// for all int/float classes. let's hope it stays that way.
|
|
|
|
if(ev->vartype = MENUVAR_INT)
|
|
|
|
((MenuEntry_Int32*)e)->variable = (int32*)addr;
|
|
|
|
else if(ev->vartype = MENUVAR_FLOAT)
|
|
|
|
((MenuEntry_Float32*)e)->variable = (float*)addr;
|
|
|
|
}
|
|
|
|
}
|