211 lines
5.4 KiB
Plaintext
211 lines
5.4 KiB
Plaintext
#ifndef MITEM_GRID_QC__
|
|
#define MITEM_GRID_QC__
|
|
|
|
//simple key/value grid (with editing).
|
|
class mitem_grid : mitem
|
|
{
|
|
//virtual void(mitem newfocus, float changedflag) item_focuschange;
|
|
virtual float(vector pos, float scan, float char, float down) item_keypress;
|
|
virtual void(vector pos) item_draw;
|
|
virtual void() item_resized;
|
|
|
|
//first child is determined from the vslider.
|
|
float grid_numchildren;
|
|
float grid_mactive;
|
|
float grid_kactive;
|
|
|
|
mitem_vslider vslider; //displayed if any client item's max[y] > our item_size[y]
|
|
|
|
virtual void(vector pos, float idx) grid_draw = 0;
|
|
virtual float(vector pos, float scan, float char, float down, float idx) grid_keypress = {return FALSE;};
|
|
virtual void(float olditem, float newitem) grid_selectionchanged = {}; //just key focus
|
|
};
|
|
|
|
//does NOT wrap.
|
|
//does NOT pass go.
|
|
static mitem(mitem item, float upwards) menu_simplenextitem =
|
|
{
|
|
mitem_frame menu = item.item_parent;
|
|
mitem prev;
|
|
if (upwards)
|
|
{
|
|
if (item)
|
|
{
|
|
item = item.item_next;
|
|
if (!item)
|
|
return __NULL__;
|
|
return item;
|
|
}
|
|
else
|
|
return __NULL__;
|
|
}
|
|
else
|
|
{
|
|
for(prev = menu.item_children; prev.item_next; prev = prev.item_next)
|
|
{
|
|
if (prev.item_next == item)
|
|
return prev;
|
|
}
|
|
return __NULL__;
|
|
}
|
|
};
|
|
|
|
float(vector pos, float scan, float char, float down) mitem_grid::item_keypress =
|
|
{
|
|
float ch;
|
|
float handled = FALSE;
|
|
|
|
if (scan >= K_MOUSE1 && scan <= K_MOUSE5 && scan != K_MWHEELUP && scan != K_MWHEELDOWN)
|
|
{
|
|
ch = grid_mactive;
|
|
if (ch != grid_kactive)
|
|
if (down && scan == K_MOUSE1) //keyboard focus follows on mouse click.
|
|
{
|
|
item_focuschange((ch>=0)?__NULL__:vslider, IF_KFOCUSED);
|
|
grid_selectionchanged(grid_kactive, ch);
|
|
grid_kactive = ch;
|
|
handled = TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ch = grid_kactive;
|
|
if (ch<0 && down)
|
|
{
|
|
//if there's no key child active, then go and pick one so you can just start using keyboard access etc.
|
|
if (grid_mactive)
|
|
ch = grid_mactive;
|
|
else
|
|
ch = 0;
|
|
item_focuschange((ch>=0)?__NULL__:vslider, IF_KFOCUSED);
|
|
grid_selectionchanged(grid_kactive, ch);
|
|
grid_kactive = ch;
|
|
}
|
|
}
|
|
|
|
if (vslider)
|
|
pos[1] -= vslider.val;
|
|
|
|
if (ch<0&&vslider)
|
|
{
|
|
if (vslider.item_keypress)
|
|
handled = vslider.item_keypress(pos + vslider.item_position, scan, char, down);
|
|
}
|
|
else if (!handled && ch >= 0 && ch < grid_numchildren)
|
|
handled = grid_keypress(pos + vslider.item_position + [0,ch]*item_scale, scan, char, down, ch);
|
|
if (!handled && down)
|
|
{
|
|
ch = grid_kactive;
|
|
switch(scan)
|
|
{
|
|
case K_MWHEELUP:
|
|
case K_MWHEELDOWN:
|
|
if (vslider) //give mwheel to the slider if its visible.
|
|
handled = vslider.item_keypress(pos + vslider.item_position, scan, char, down);
|
|
break;
|
|
case K_HOME:
|
|
ch = 0;
|
|
break;
|
|
case K_END:
|
|
ch = grid_numchildren-1;
|
|
break;
|
|
case K_PGUP:
|
|
ch = max(0, grid_kactive-5);
|
|
break;
|
|
case K_PGDN:
|
|
ch = min(grid_numchildren-1, grid_kactive+5);
|
|
break;
|
|
case K_UPARROW:
|
|
ch = max(grid_kactive-1, 0);
|
|
break;
|
|
case K_DOWNARROW:
|
|
ch = min(grid_kactive+1, grid_numchildren-1);
|
|
break;
|
|
}
|
|
if (ch != grid_kactive)
|
|
{
|
|
grid_selectionchanged(grid_kactive, ch);
|
|
grid_kactive = ch;
|
|
handled = TRUE;
|
|
}
|
|
}
|
|
|
|
return handled;
|
|
};
|
|
void() mitem_grid::item_resized =
|
|
{
|
|
float clientheight = grid_numchildren * item_scale;
|
|
if (clientheight > item_size[1])
|
|
{
|
|
if (!vslider)
|
|
vslider = spawn(mitem_vslider, val:0, stride:8);
|
|
vslider.maxv = clientheight - item_size[1];
|
|
}
|
|
else if (vslider)
|
|
{
|
|
vslider.item_remove();
|
|
vslider = (mitem_vslider)__NULL__;
|
|
}
|
|
}
|
|
|
|
void(vector pos) mitem_grid::item_draw =
|
|
{
|
|
local vector omin = ui.drawrectmin, omax = ui.drawrectmax;
|
|
local vector clientpos;
|
|
local vector clientsize;
|
|
|
|
clientpos = pos;
|
|
clientsize = this.item_size;
|
|
if (vslider)
|
|
{
|
|
//scroll+shrink the client area to fit the slider on it.
|
|
clientpos[1] -= vslider.val;
|
|
clientsize[0] -= vslider.item_size[0];
|
|
}
|
|
|
|
if (ui.mousepos != ui.oldmousepos)
|
|
{
|
|
local mitem newfocus = __NULL__;
|
|
grid_mactive = floor((ui.mousepos[1]-clientpos_y)/item_scale);
|
|
|
|
if (vslider)
|
|
if (mouseinbox(pos + [clientsize[0], 0], vslider.item_size))
|
|
{
|
|
newfocus = vslider;
|
|
grid_mactive = -1;
|
|
}
|
|
this.item_focuschange(newfocus, IF_MFOCUSED);
|
|
}
|
|
|
|
//clip the draw rect to our area, so children don't draw outside it. don't draw if its inverted.
|
|
if (pos_x > ui.drawrectmin[0])
|
|
ui.drawrectmin[0] = pos_x;
|
|
if (pos_y > ui.drawrectmin[1])
|
|
ui.drawrectmin[1] = pos_y;
|
|
if (pos_x+clientsize_x < ui.drawrectmax[0])
|
|
ui.drawrectmax[0] = pos_x+clientsize_x;
|
|
if (pos_y+clientsize_y < ui.drawrectmax[1])
|
|
ui.drawrectmax[1] = pos_y+clientsize[1];
|
|
if (ui.drawrectmax[0] > ui.drawrectmin[0] && ui.drawrectmax[1] > ui.drawrectmin[1])
|
|
{
|
|
ui.setcliparea(ui.drawrectmin[0], ui.drawrectmin[1], ui.drawrectmax[0] - ui.drawrectmin[0], ui.drawrectmax[1] - ui.drawrectmin[1]);
|
|
float c = max(0,floor((ui.drawrectmin[1]-clientpos_y)/item_scale)); //calculate how many we can skip
|
|
for (clientpos_y += item_scale * c; c < grid_numchildren; c++, clientpos_y += item_scale)
|
|
{
|
|
if (clientpos_y >= ui.drawrectmax[1])
|
|
break;
|
|
grid_draw(clientpos, c);
|
|
}
|
|
ui.setcliparea(omin_x, omin_y, omax_x - omin_x, omax_y - omin_y);
|
|
|
|
if (vslider)
|
|
{
|
|
vslider.item_size[1] = clientsize[1];
|
|
vslider.item_draw(pos + [clientsize[0], 0]);
|
|
}
|
|
}
|
|
ui.drawrectmin = omin;
|
|
ui.drawrectmax = omax;
|
|
};
|
|
#endif
|