396 lines
12 KiB
Plaintext
396 lines
12 KiB
Plaintext
#ifdef MENU
|
|
#define cltime time
|
|
#endif
|
|
|
|
#ifdef CSQC
|
|
#define csqconly
|
|
#else
|
|
#define csqconly __strip
|
|
#endif
|
|
#ifdef MENU
|
|
#define menuonly
|
|
#else
|
|
#define menuonly __strip
|
|
#endif
|
|
|
|
|
|
|
|
//#include "mitems.qh"
|
|
|
|
__strip var float dp_workarounds;
|
|
|
|
#define IF_SELECTABLE (1<<0) //can accept KFOCUSED/MFOCUSED and key events etc. cannot be selected otherwise.
|
|
#define IF_INTERACT (1<<1) //generic interaction flag for use by the widgets themselves.
|
|
#define IF_RESIZABLE (1<<2) //may be resized, either by parent (it takes the full space) or by user.
|
|
#define IF_MFOCUSED (1<<3) //mouse is currently sitting over it
|
|
#define IF_KFOCUSED (1<<4) //has keyboard focus
|
|
#define IF_NOKILL (1<<5) //kill button is disabled. move to frame/menu?
|
|
#define IF_DRAGGABLE (1<<6) //can be dragged. this is stupid.
|
|
#define IF_DROPPABLE (1<<7) //dragged items can be dropped on this item.
|
|
#define IF_CLIENTMOVED (1<<8) //recalc required client dimensions (and toggle scrollbars if needed). move to frame?
|
|
#define IF_CENTERALIGN (1<<9) //
|
|
#define IF_RIGHTALIGN (1<<10) //
|
|
#define IF_NOCURSOR (1<<11) //when mgrabbed, the cursor will not be shown
|
|
#define IF_ISFRAME (1<<12) //is derived from mitem_frame (helps debugging recurion).
|
|
#define IF_FOCUSFOLLOWSMOUSE (1<<13) //on frames, child keyboard focus (mostly) follows the mouse cursor. not like windows, but handy on things with lots of buttons. annoying on the desktop. move to frame?
|
|
#define IF_INVISIBLE (1<<14)
|
|
|
|
#define RS_X_MIN_PARENT_MIN 0x0000
|
|
#define RS_X_MIN_PARENT_MID 0x0001
|
|
#define RS_X_MIN_PARENT_MAX 0x0002
|
|
#define RS_X_FRACTION 0x0004
|
|
|
|
#define RS_X_MAX_OWN_MIN 0x0000
|
|
#define RS_X_MAX_PARENT_MIN 0x0010
|
|
#define RS_X_MAX_PARENT_MID 0x0020
|
|
#define RS_X_MAX_PARENT_MAX 0x0040
|
|
|
|
#define RS_Y_MIN_PARENT_MIN 0x0000
|
|
#define RS_Y_MIN_PARENT_MID 0x0100
|
|
#define RS_Y_MIN_PARENT_MAX 0x0200
|
|
#define RS_Y_FRACTION 0x0400
|
|
|
|
#define RS_Y_MAX_OWN_MIN 0x0000
|
|
#define RS_Y_MAX_PARENT_MIN 0x1000
|
|
#define RS_Y_MAX_PARENT_MID 0x2000
|
|
#define RS_Y_MAX_PARENT_MAX 0x4000
|
|
|
|
//the 3d effect needs some sort of fake lighting values.
|
|
//these are for bumps. invert for inset things.
|
|
#define TD_TOP '0.8 0.8 0.8'
|
|
#define TD_LFT '0.6 0.6 0.6'
|
|
#define TD_RGT '0.2 0.2 0.2'
|
|
#define TD_BOT '0.0 0.0 0.0'
|
|
|
|
#ifndef MENUBACK_RGB
|
|
#define MENUBACK_RGB '0.4 0.365 0.29'
|
|
//#define MENUBACK_RGB '0.5 0.5 0.6'
|
|
#endif
|
|
#ifndef MENUBACK_ALPHA
|
|
#define MENUBACK_ALPHA 0.8
|
|
#endif
|
|
|
|
//#ifdef TARGET_FTE
|
|
//#pragma TARGET FTE
|
|
//#endif
|
|
|
|
//#define HEIRACHYDEBUG //enable this and press f1 to print out the current menuitem tree
|
|
|
|
class mitem_frame;
|
|
.vector mins; //gravity mins
|
|
.vector maxs; //gravity mins
|
|
class mitem
|
|
{
|
|
void() mitem;
|
|
virtual void(vector pos) item_draw;
|
|
virtual float(vector pos, float scan, float char, float down) item_keypress = {return FALSE;};
|
|
virtual void(mitem newfocus, float changedflag) item_focuschange; //move into frame?
|
|
virtual string(string key) get;
|
|
virtual void(string key, string newval) set;
|
|
virtual float(string key) isvalid;
|
|
virtual void() item_remove;
|
|
virtual void() item_resized;
|
|
float item_flags; //contains IF_ flags
|
|
vector item_position; //relative to the parent's client area. which is admittedly not well defined...
|
|
vector item_size; //interaction bounding box.
|
|
float item_scale; //text etc scale
|
|
vector item_rgb; //colours!
|
|
float item_alpha; //transparency value
|
|
string item_text; //used as a unique identifier
|
|
string item_command; //something to do when clicked. move out?
|
|
mitem_frame item_parent; //the item that contains us. make mitem_frame?
|
|
mitem item_next; //the next child within the parent
|
|
|
|
float resizeflags;
|
|
|
|
|
|
static void() totop;
|
|
};
|
|
|
|
//this struct is used to access the various drawing functions. this allows the functions to be replaced for worldspace stuff
|
|
typedef struct uiinfo_s
|
|
{
|
|
void(float min_x, float min_y, float max_x, float max_y) setcliparea;
|
|
float(vector min, string imagename, vector size, vector col, float alph, float drawflag) drawpic;
|
|
float(vector min, vector size, vector col, float alph, float drawflag) drawfill;
|
|
float(vector min, float charcode, vector scale, vector col, float alph, float drawflag) drawcharacter;
|
|
float(vector min, string text, vector scale, vector col, float alph, float drawflag) drawstring;
|
|
|
|
mitem kgrabs; //kfocused or mfocused or both or none to say what sort of grabs is in effect.
|
|
mitem mgrabs; //says who has stolen all input events.
|
|
|
|
float ctrlheld; //ctrl is held.
|
|
float shiftheld; //shift is held.
|
|
float mousedown; //which mouse buttons are currently held.
|
|
vector oldmousepos; //old mouse position, to track whether its moved.
|
|
vector mousepos; //current mouse position.
|
|
vector screensize; //total screen size
|
|
vector drawrectmin; //minimum scissor region, to clamp children to within the confines of its parent.
|
|
vector drawrectmax; //maximum scissor region
|
|
|
|
#ifndef MENU
|
|
//menuqc has no concept of the world and cannot display or query 3d positions nor projections. Any related UI elements are thus not available to menuqc.
|
|
//these globals are not part of the ui struct either, because they're illegal in world UIs.
|
|
float havemouseworld; //whether the mouseworld stuff is valid - ie: that the cursor is in a 3d view
|
|
vector mouseworldnear; //position of the mouse cursor upon the near clip plane in world space
|
|
vector mouseworldfar; //position of the mouse cursor upon the far(ish) clip plane in world space
|
|
#endif
|
|
} uiinfo_t;
|
|
var uiinfo_t ui =
|
|
{
|
|
drawsetcliparea,
|
|
drawpic,
|
|
drawfill,
|
|
drawcharacter,
|
|
drawstring
|
|
};
|
|
|
|
|
|
|
|
void() queryscreensize =
|
|
{
|
|
#pragma warning disable F333
|
|
#ifdef MENU
|
|
//there is no proper way to do this.
|
|
//fte thus has special checks for these cvars, and they should not be autocvars if you want them to work properly.
|
|
ui.screensize[0] = cvar("vid_conwidth");
|
|
ui.screensize[1] = cvar("vid_conheight");
|
|
ui.screensize[2] = 0;
|
|
#endif
|
|
#ifdef CSQC
|
|
#ifndef CSQC_SIMPLE
|
|
//make sure the screensize is set.
|
|
normalize('0 0 1'); //when getproperty fails to return a meaningful value, make sure that we don't read some random stale value.
|
|
ui.screensize = (vector)getproperty(VF_SCREENVSIZE);
|
|
if (ui.screensize[2]) //lingering return value from normalize.
|
|
{
|
|
ui.screensize[2] = 0;
|
|
ui.screensize[0] = cvar("vid_conwidth");
|
|
ui.screensize[1] = cvar("vid_conheight");
|
|
}
|
|
#endif
|
|
#endif
|
|
#pragma warning enable F333
|
|
};
|
|
|
|
//helper function
|
|
float(vector minp, vector sz) mouseinbox =
|
|
{
|
|
if (ui.mousepos[0] < minp_x)
|
|
return FALSE;
|
|
if (ui.mousepos[1] < minp_y)
|
|
return FALSE;
|
|
if (ui.mousepos[0] >= minp_x + sz_x)
|
|
return FALSE;
|
|
if (ui.mousepos[1] >= minp_y + sz_y)
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
};
|
|
|
|
|
|
void(mitem ch, vector parentsize) mitem_parentresized;
|
|
|
|
#include "mitem_frame.qc"
|
|
void() mitem::item_resized =
|
|
{
|
|
if (this.item_parent)
|
|
this.item_parent.item_flags |= IF_CLIENTMOVED;
|
|
};
|
|
|
|
|
|
#ifdef HEIRACHYDEBUG
|
|
void mitem_printnode(float depth, mitem root, string prefix)
|
|
{
|
|
mitem ch;
|
|
string col = "";
|
|
if (root.item_flags & IF_KFOCUSED && root.item_flags & IF_MFOCUSED)
|
|
col = "^2";
|
|
else if (root.item_flags & IF_KFOCUSED)
|
|
col = "^5";
|
|
else if (root.item_flags & IF_MFOCUSED)
|
|
col = "^3";
|
|
|
|
print(sprintf("%s%s%i:%s%s\n", col, strpad(depth, ""), root, prefix, root.item_text));
|
|
|
|
//can only recurse into items which are actually frames.
|
|
if (root.item_flags & IF_ISFRAME)
|
|
{
|
|
mitem_frame fr = (mitem_frame)root;
|
|
depth+=1;
|
|
for(ch = fr.item_children; ch; ch = ch.item_next)
|
|
{
|
|
if (ch.item_parent != root)
|
|
print("corrupt parent\n");
|
|
if (ch.item_next == ch)
|
|
{
|
|
print("infinite loop\n");
|
|
break;
|
|
}
|
|
string pre = "";
|
|
if (fr.item_mactivechild == ch)
|
|
pre = strcat(pre, "m");
|
|
if (fr.item_kactivechild == ch)
|
|
pre = strcat(pre, "k");
|
|
if (pre)
|
|
pre = strcat(pre, " ");
|
|
mitem_printnode(depth, ch, pre);
|
|
}
|
|
}
|
|
};
|
|
void mitem_printtree(mitem_frame root, string from, float line)
|
|
{
|
|
print(sprintf("%s:%g\n", from, line));
|
|
mitem_printnode(0, root, "");
|
|
}
|
|
#endif
|
|
|
|
string(string key) mitem::get =
|
|
{
|
|
//walk through to the parent for menu parents to track all this.
|
|
if (this.item_parent)
|
|
return this.item_parent.get(key);
|
|
else //no parent, just assume its a cvar.
|
|
return cvar_string(key);
|
|
};
|
|
void(string key, string newval) mitem::set =
|
|
{
|
|
//walk through to the parent for menu parents to track all this.
|
|
if (this.item_parent)
|
|
this.item_parent.set(key, newval);
|
|
else //no parent, just assume its a cvar.
|
|
cvar_set(key, newval);
|
|
};
|
|
float(string key) mitem::isvalid =
|
|
{
|
|
//walk through to the parent for menu parents to track all this.
|
|
if (this.item_parent)
|
|
return this.item_parent.isvalid(key);
|
|
else //no parent, just assume its a cvar.
|
|
return cvar_type(key);
|
|
};
|
|
|
|
|
|
/***************************************************************************
|
|
basic 'null' item, for completeness, every other item logically inherits from this.
|
|
drawing this just shows its text right-aligned at w/2. most things will want to override this.
|
|
*/
|
|
void(mitem newfocus, float flag) mitem::item_focuschange =
|
|
{
|
|
};
|
|
|
|
//z order is determined by the list order. the ones at the end (oldest) are on top.
|
|
void() mitem::totop =
|
|
{
|
|
mitem_frame p = item_parent;
|
|
mitem prev;
|
|
//unlink it
|
|
if (p.item_children == this)
|
|
prev = p.item_children = item_next;
|
|
else
|
|
{
|
|
for (prev = p.item_children; prev; prev = prev.item_next)
|
|
{
|
|
if (prev.item_next == this)
|
|
{
|
|
prev.item_next = item_next;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
item_next = __NULL__;
|
|
if (prev)
|
|
{
|
|
//add it on the end
|
|
while(prev.item_next)
|
|
prev = prev.item_next;
|
|
prev.item_next = this;
|
|
}
|
|
else
|
|
p.item_children = this;
|
|
};
|
|
|
|
vector(mitem it) menuitem_textcolour =
|
|
{
|
|
local vector col;
|
|
col = it.item_rgb;
|
|
if (!(it.item_flags & IF_SELECTABLE) && it.item_keypress && it.item_command != "")
|
|
col *= 0.2;
|
|
else
|
|
{
|
|
if (it.item_flags & IF_MFOCUSED)
|
|
col_z = 0;
|
|
if (it.item_flags & IF_KFOCUSED)
|
|
col_x = 0;
|
|
}
|
|
return col;
|
|
};
|
|
void(vector pos) mitem::item_draw =
|
|
{
|
|
vector col;
|
|
pos_x += this.item_size[0] / 2 - stringwidth(this.item_text, TRUE, '1 1 0'*this.item_scale) - 8;
|
|
col = menuitem_textcolour(this);
|
|
ui.drawstring(pos, this.item_text, '1 1 0' * this.item_scale, col, this.item_alpha, 0);
|
|
};
|
|
void() mitem::item_remove =
|
|
{
|
|
local mitem ch;
|
|
//any children got removed by the frame code.
|
|
|
|
//make sure the item is removed from its parent.
|
|
local mitem_frame p = this.item_parent;
|
|
if (p)
|
|
{
|
|
if (p.item_exclusive == this)
|
|
p.item_exclusive = __NULL__;
|
|
|
|
if (p.item_children == this)
|
|
p.item_children = this.item_next;
|
|
else
|
|
{
|
|
for(ch = p.item_children; ch; ch = ch.item_next)
|
|
{
|
|
if (ch.item_next == this)
|
|
{
|
|
ch.item_next = this.item_next;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//pick a different item on the parent.
|
|
if (p.item_mactivechild == this)
|
|
p.item_focuschange(__NULL__, IF_MFOCUSED);
|
|
if (p.item_kactivechild == this)
|
|
{
|
|
for (ch = p.item_children; ch; ch = ch.item_next)
|
|
{
|
|
if (ch.item_flags & IF_SELECTABLE)
|
|
break;
|
|
}
|
|
p.item_focuschange(ch, IF_KFOCUSED);
|
|
}
|
|
if (ui.kgrabs == this)
|
|
ui.kgrabs = __NULL__;
|
|
if (ui.mgrabs == this)
|
|
ui.mgrabs = __NULL__;
|
|
remove((entity)this);
|
|
|
|
//force mousefocus to update
|
|
ui.oldmousepos = '-1 -1';
|
|
};
|
|
void() mitem::mitem =
|
|
{
|
|
if (!this.item_scale)
|
|
this.item_scale = 1;
|
|
if (!this.item_rgb)
|
|
this.item_rgb = '1 1 1';
|
|
if (!this.item_alpha)
|
|
this.item_alpha = 1;
|
|
|
|
//force mousefocus to update
|
|
ui.oldmousepos = '-1 -1';
|
|
};
|