engine/quakec/menusys/menusys/mitem_combo.qc

375 lines
9.1 KiB
Plaintext

/***************************************************************************
combo item.
No longer using pointers, now using a tokenized string. Less efficient, but saves creating lots of different arrays, just pass a string.
The string list is doubled, first is the actual value, second is the friendly text.
Will show actual value when focused, and will show readable value when not.
The possible values is a separate popup.
*/
//FIXME: should probably set up a grabs to intercept right-click / escape outside of the item
class mitem_combo;
class mitem_combo_popup;
class mitem_combo : mitem_edit
{
virtual void(vector pos) item_draw;
virtual float(vector pos, float scan, float char, float down) item_keypress;
virtual void(mitem newfocus, float changedflag) item_focuschange;
mitem_combo_popup cpopup;
string mstrlist;
float firstrow;
float visrows;
virtual void() item_remove;
virtual void() item_resized =
{
if (isvalid(item_command))
item_flags |= IF_SELECTABLE;
else
item_flags &= ~IF_SELECTABLE;
super::item_resized();
};
void() mitem_combo =
{
spos = -1;
};
};
class mitem_combo_popup : mitem
{
virtual void(vector pos) item_draw;
virtual float(vector pos, float scan, float char, float down) item_keypress;
virtual void(mitem newfocus, float changedflag) item_focuschange;
mitem_combo pcombo;
mitem_vslider pslider;
float slidercache;
virtual void() item_remove =
{
if (pslider)
pslider.item_remove();
if (pcombo)
pcombo.cpopup = 0;
super::item_remove();
};
};
void() mitem_combo::item_remove =
{
mitem_combo_popup p = cpopup;
if (p)
p.item_remove();
strunzone(mstrlist);
item_text = __NULL__;
item_command = __NULL__;
super::item_remove();
};
void(vector pos) mitem_combo::item_draw =
{
local float i, v;
local string curval = get(item_command);
vector rgb = item_rgb;
if (!(item_flags & IF_SELECTABLE))
rgb *= 0.2;
if (cpopup)
cpopup.item_position = pos + [item_size_x / 2, item_size_y];
if (spos >= 0)
{
super::item_draw(pos);
return;
}
else
mitem::item_draw(pos);
v = tokenize(mstrlist);
//display the friendly string if the current value matches
// if (!(item_flags & IF_KFOCUSED) && (!cfriend || !(cfriend.item_flags & IF_KFOCUSED)))
{
for (i = 0; i < v; i+=2)
{
if (argv(i) == curval)
{
curval = argv(i+1);
break;
}
}
}
pos_x += item_size_x / 2;
/* //border
ui.drawfill(pos, [item_size_x/2, 1], TD_BOT, item_alpha, 0);
ui.drawfill(pos, [1, item_size_y - 1], TD_RGT, item_alpha, 0);
ui.drawfill(pos + [item_size_x/2-1, 1], [1, item_size_y - 1], TD_LFT, item_alpha, 0);
ui.drawfill(pos + [0, item_size_y-1], [item_size_x/2, 1], TD_TOP, item_alpha, 0);
*/
//silly strings need to get cut off properly.
ui.setcliparea(pos[0], pos[1], item_size_x/2, item_size_y);
pos_y += (item_size_y - item_scale)*0.5;
pos_x += 1;
ui.drawstring(pos, curval, '1 1 0' * item_scale, rgb, item_alpha, 0);
ui.setcliparea(ui.drawrectmin[0], ui.drawrectmin[1], ui.drawrectmax[0] - ui.drawrectmin[0], ui.drawrectmax[1] - ui.drawrectmin[1]);
};
void(mitem newfocus, float flag) mitem_combo::item_focuschange =
{
if (!cpopup || !(flag & IF_KFOCUSED))
return; //don't care
if (newfocus != (mitem)this && newfocus != (mitem)cpopup)
{
cpopup.item_size = cpopup.maxs = '0 0';
cpopup.item_flags &~= IF_SELECTABLE;
}
spos = -1;
};
void(mitem newfocus, float flag) mitem_combo_popup::item_focuschange =
{
pcombo.item_focuschange(newfocus, flag);
};
void(vector pos) mitem_combo_popup::item_draw =
{
vector col;
if (item_size_y < 1)
return;
local mitem_combo f = pcombo;
item_command = f.item_command;
local string curval = f.get(f.item_command);
local float i, m, c, v;
vector popupsize = item_size;
if (!((f.item_flags | item_flags) & IF_KFOCUSED))
{
item_size = maxs = '0 0';
item_flags &~= IF_SELECTABLE;
return;
}
ui.drawfill(pos, popupsize, item_rgb, item_alpha, 0);
/* //border
ui.drawfill(pos, [popupsize_x, 1], TD_BOT, item_alpha, 0);
ui.drawfill(pos, [1, popupsize_y - 1], TD_RGT, item_alpha, 0);
ui.drawfill(pos + [popupsize_x-1, 1], [1, popupsize_y - 1], TD_LFT, item_alpha, 0);
ui.drawfill(pos + [0, popupsize_y-1], [popupsize_x, 1], TD_TOP, item_alpha, 0);
*/ pos_x += 1;
v = tokenize(f.mstrlist);
for (c = 0; c < v; c += 2)
if (argv(c) == curval)
break;
if (c >= v)
c = -1;
i = f.firstrow;
i = i*2;
if (!f.visrows)
i = 0;
else if (c >= 0)
{
//bound the displayed position
if (c < i)
i = c;
if (i < c - (f.visrows-1)*2)
i = c - (f.visrows-1)*2;
}
f.firstrow = floor(i*0.5);
if (v > f.visrows*2)
{
if (!pslider)
{
slidercache = -2;
pslider = spawn(mitem_vslider);
}
}
else if (pslider)
{
pslider.item_remove();
pslider = __NULL__;
}
if (pslider)
{
popupsize_x -= pslider.item_size_x;
if (slidercache != c)
{ //current index changed, force the slider to the active item.
slidercache = c;
pslider.val = f.firstrow;
}
pslider.maxv = v/2 - f.visrows;
pslider.item_size_y = popupsize_y;
pslider.item_draw(pos + [popupsize_x, 0]);
}
//constrain the drawing so it doesn't overflow the popup
ui.setcliparea(pos[0], pos[1], popupsize[0], popupsize[1]);
if (pslider)
{
f.firstrow = i = floor(pslider.val);
m = ((i!=pslider.val) + i + f.visrows)*2;
i *= 2;
pos[1] -= (pslider.val-i/2) * item_scale;
}
else m = i + f.visrows*2;
for (; i < m && i < v ; i+=2)
{
col = f.item_rgb;
if (item_flags & IF_MFOCUSED)
if (mouseinbox(pos, [popupsize_x, item_scale]))
col_z = 0;
if (c == i)
col_x = 0;
ui.drawstring(pos, argv(i+1), '1 1 0' * item_scale, col, f.item_alpha, 0);
pos_y += item_scale;
}
//reset the clip area
ui.setcliparea(ui.drawrectmin[0], ui.drawrectmin[1], ui.drawrectmax[0] - ui.drawrectmin[0], ui.drawrectmax[1] - ui.drawrectmin[1]);
};
float(vector pos, float scan, float char, float down) mitem_combo_popup::item_keypress =
{
if (pslider && scan == K_MOUSE1)
{
vector sliderpos = pos + [item_size_x-pslider.item_size_x,0];
if (mouseinbox(pos + [item_size_x-pslider.item_size_x,0], pslider.item_size))
return pslider.item_keypress(sliderpos, scan, char, down);
}
return pcombo.item_keypress(pos - [0, pcombo.item_size_y], scan, char, down);
};
float(vector pos, float scan, float char, float down) mitem_combo::item_keypress =
{
if (!down)
return FALSE;
local string curval = get(item_command);
local float i, c;
local float f;
c = tokenize(mstrlist);
//find which one is active
for (i = 0; i < c; i+=2)
{
if (argv(i) == curval)
{
break;
}
}
if (scan == K_ESCAPE || scan == K_MOUSE2)
{
if (cpopup)
{
cpopup.item_remove();
return TRUE;
}
return FALSE;
}
else if (scan == K_MWHEELUP || (scan == K_UPARROW && cpopup))
{
i -= 2;
if (i < 0)
i = c - 2;
curval = argv(i);
}
else if (scan == K_MWHEELDOWN || (scan == K_DOWNARROW && cpopup))
{
i += 2;
if (i >= c)
i = 0;
curval = argv(i);
}
else if (scan == K_MOUSE1 || scan == K_ENTER)
{
if (scan == K_ENTER && cpopup)
{
cpopup.item_remove();
return TRUE;
}
visrows = ((c>18)?18/2:c/2);
if (!cpopup)
{
cpopup = spawn(mitem_combo_popup);
cpopup.pcombo = this;
cpopup.item_scale = 8;
cpopup.item_rgb = MENUBACK_RGB;
cpopup.item_alpha = MENUBACK_ALPHA;
pos = item_position;
mitem_frame fr = item_parent;
while (fr.item_parent)
{ //try to inject the combo thingie into the desktop item. this is to avoid scissoring.
pos += fr.item_position;
fr = fr.item_parent;
}
fr.addr(cpopup, RS_X_MIN_PARENT_MIN | RS_X_MAX_OWN_MIN | RS_Y_MIN_PARENT_MIN | RS_Y_MAX_OWN_MIN, pos + [self.item_size_x / 2, self.item_size_y], [item_size_x*0.5, item_size_y*visrows]);
}
cpopup.item_size = cpopup.maxs = [item_size_x*0.5, item_size_y*visrows];
cpopup.item_flags |= IF_SELECTABLE;
cpopup.totop();
if (scan == K_MOUSE1 && (cpopup.item_flags & IF_MFOCUSED))
{
//if they clicked inside the popup, change the selected item.
f = ui.mousepos[1] - (pos_y + item_size_y);
f /= cpopup.item_scale;
f += firstrow;
i = floor(f) * 2;
if (i < c)
{
curval = argv(i);
cpopup.item_flags &~= IF_SELECTABLE;
cpopup.item_size = cpopup.maxs = '0 0';
item_parent.item_focuschange(this, IF_MFOCUSED|IF_KFOCUSED);
cpopup.item_remove();
}
}
}
else
{
if (spos < 0)
{
spos = strlen(curval);
if (!super::item_keypress(pos, scan, char, down))
{
spos = -1;
return FALSE;
}
return TRUE;
}
else return super::item_keypress(pos, scan, char, down);
}
set(item_command, curval);
return TRUE;
};
mitem_combo(string text, string command, vector sz, string valuelist) menuitemcombo_spawn =
{
mitem_combo n = spawn(mitem_combo);
n.item_scale = sz_y;
n.item_text = text;
n.item_size = sz;
n.mstrlist = strzone(valuelist);
n.item_command = command;
if (n.isvalid(command))
n.item_flags |= IF_SELECTABLE;
return n;
};