375 lines
9.1 KiB
Plaintext
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;
|
|
};
|