fteqw/quakec/menusys/menusys/mitems.qc

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';
};