fteqw/quakec/menusys/menusys/mitem_menu.qc

333 lines
9.5 KiB
Plaintext

/***************************************************************************
window-like menu.
interactable - basically just a container for the items in the menu, but also handles killing them+itself when the user presses escape.
these items will automatically be added to the desktop/workspace thing
Call it.item_remove() to pop the menu.
Regular items can be added to the menu by first spawning them, then calling menu_additem to actually add it to the desired menu in the right place.
*/
class mitem_menu : mitem_frame
{
vector draginfo; //x, y, resizeflags={0:none, 1:left, 2:bottom, 4:right, 8:top, 16:move}
virtual float(vector pos, float scan, float char, float down) item_keypress;
virtual void(mitem newfocus, float flag) item_focuschange;
virtual void(vector pos) item_draw;
virtual void() item_remove =
{
strunzone(item_text);
super::item_remove();
};
nonvirtual float(vector pos) whichgrabhandle;
nonvirtual void(vector pos, float handle) drawgrabhandle;
void() mitem_menu =
{
item_text = strzone(item_text);
item_scale = 8;
item_rgb = MENUBACK_RGB;
item_alpha = MENUBACK_ALPHA;
if (item_framesize == '0 0 0')
item_framesize = '4 16 4';
item_size[0] += item_framesize[0];
item_size[1] += item_framesize[1]+item_framesize[2];
item_flags |= IF_SELECTABLE;
#ifdef CSQC
cprint("");
#endif
};
//same as mitem, but does not propogate to parent.
virtual string(string key) get =
{
return cvar_string(key);
};
virtual void(string key, string newval) set =
{
cvar_set(key, newval);
};
};
void(mitem newfocus, float flag) mitem_menu::item_focuschange =
{
if (flag & IF_KFOCUSED)
{
//move the window to the top of the z-order when it gets focus.
if (item_flags & IF_KFOCUSED)
totop();
//release grabs if someone else took focus.
else if (ui.mgrabs == this)
{
ui.mgrabs = __NULL__;
draginfo[2] = 0;
}
}
super::item_focuschange(newfocus, flag);
};
void(vector pos, float handle) mitem_menu::drawgrabhandle =
{
if ((handle & 3) == 3)
{
//bottom left
drawfill(pos + [0, item_size[1]-item_framesize[2]], [item_framesize[0], item_framesize[2]], '1 1 0', 1, 0);
handle &~= 3;
}
if ((handle & 6) == 6)
{
//bottom right
drawfill(pos + [item_size[0]-item_framesize[0], item_size[1]-item_framesize[2]], [item_framesize[0], item_framesize[2]], '1 1 0', 1, 0);
handle &~= 6;
}
if (handle & 1)
{
//left
drawfill(pos + [0, 0], [item_framesize[0], item_size[1]], '1 1 0', 1, 0);
}
if (handle & 2)
{
//bottom
drawfill(pos + [0, item_size[1]-item_framesize[2]], [item_size[0], item_framesize[2]], '1 1 0', 1, 0);
}
if (handle & 4)
{
//right
drawfill(pos + [item_size[0]-item_framesize[0], 0], [item_framesize[0], item_size[1]], '1 1 0', 1, 0);
}
};
float(vector pos) mitem_menu::whichgrabhandle =
{
//handle dragging and resizing.
if (mouseinbox(pos, [item_size[0]-item_framesize[1], item_framesize[1]]))
{
//drag
return 16;
}
else if (this.item_flags & IF_RESIZABLE)
{
if (mouseinbox(pos + [0, item_size[1]-item_framesize[2]], [item_framesize[0], item_framesize[2]]))
{
//bottom-left
return 3;
}
else if (mouseinbox(pos + [item_size[0]-item_framesize[0], item_size[1]-item_framesize[2]], [item_framesize[0], item_framesize[2]]))
{
//bottom-right
return 6;
}
else if (mouseinbox(pos + [0, 0], [item_framesize[0], item_size[1]]))
{
//left
return 1;
}
else if (mouseinbox(pos + [0, item_size[1]-item_framesize[2]], [item_size[0], item_framesize[2]]))
{
//bottom
return 2;
}
else if (mouseinbox(pos + [item_size[0]-item_framesize[0], 0], [item_framesize[0], item_size[1]]))
{
//right
return 4;
}
}
return 0;
};
float(vector pos, float scan, float char, float down) mitem_menu::item_keypress =
{
if (scan == K_MOUSE1 && (down&1))
{
if (!(item_flags & IF_NOKILL) && mouseinbox(pos + [item_size[0]-item_framesize[1], 0], item_framesize[1] * '1 1'))
{
localcmd(strcat(item_command, "\n")); //console command to exec if someone clicks the close button.
this.item_remove();
return TRUE;
}
draginfo[2] = this::whichgrabhandle(pos);
if (draginfo[2])
ui.mgrabs = this;
if (draginfo[2] & (1|16))
draginfo[0] = ui.mousepos[0] - pos_x;
if (draginfo[2] & 2)
draginfo[1] = (ui.mousepos[1] - pos_y - item_size[1]) + item_position[1];
if (draginfo[2] & 4)
draginfo[0] = ui.mousepos[0] - pos_x - item_size[0] + item_position[0];
if (draginfo[2] & (8|16))
draginfo[1] = ui.mousepos[1] - pos_y;
if (draginfo[2])
return TRUE;
}
else if (scan == K_MOUSE1 && draginfo[2])
{
if (!draginfo[2] && (item_flags & IF_RESIZABLE))
{
//if they dropped the window at the top of the screen, make sure its fullscreened to match how it was drawn...
queryscreensize();
if (ui.mousepos[1] < 8)
item_size = ui.screensize;
if (this.item_resized)
item_resized();
}
ui.mgrabs = __NULL__;
draginfo[2] = 0;
return TRUE;
}
if (!super::item_keypress(pos, scan, char, down))
{
down &= 1;
if (scan == K_ESCAPE && down)
{
if (this.item_flags & IF_NOKILL)
return FALSE;
else
{
localcmd(strcat(this.item_command, "\n")); //console command to exec if someone clicks the close button.
this.item_remove();
return TRUE;
}
}
else if (scan == K_UPARROW && down)
menu_selectnextitem(this, TRUE);
else if (scan == K_DOWNARROW && down)
menu_selectnextitem(this, FALSE);
else if (scan == K_MOUSE2 && down && !(this.item_flags & IF_NOKILL))
{ //unhandled right click closes menus, if we're allowed
localcmd(strcat(item_command, "\n")); //console command to exec if someone clicks the close button.
this.item_remove();
return TRUE;
}
else if (scan >= K_MOUSE1 && scan <= K_MWHEELDOWN)
return TRUE; //don't let the parent take unhandled mouse events.
else
return FALSE;
}
return TRUE;
};
void(vector pos) mitem_menu::item_draw =
{
float inset;
vector efsize = item_size;
if (draginfo[2])
{
if (draginfo[2] & 1)
{ //resize left
local float nmax = item_position[0] + item_size[0];
local float nmin = item_position[0] + item_size[0];
nmin = ui.mousepos[0] - (pos_x - item_position[0]) - draginfo[0];
if (nmax-nmin < 128)
nmin = nmax - 128;
pos_x -= item_position[0];
item_position[0] = nmin;
pos_x += item_position[0];
item_size[0] = nmax - nmin;
if (item_resized)
item_resized();
}
if (draginfo[2] & 2)
{ //resize bottom
item_size[1] = ui.mousepos[1] - (pos_y - item_position[1]) - draginfo[1];
if (item_size[1] < 16)
item_size[1] = 16;
if (item_resized)
item_resized();
}
if (draginfo[2] & 4)
{ //resize right
item_size[0] = ui.mousepos[0] - (pos_x - item_position[0]) - draginfo[0];
if (item_size[0] < 128)
item_size[0] = 128;
if (item_resized)
item_resized();
}
efsize = item_size;
if (draginfo[2]&16)
{
queryscreensize();
if (ui.mousepos[1] < 8 && (item_flags & IF_RESIZABLE))
{
//make it look as though its fullscreen, without destroying its actual size.
//yes, we will fake-resize everything inside.
item_position = '0 0';
item_size = ui.screensize;
if (this.item_resized)
this.item_resized();
item_size = efsize;
efsize = ui.screensize;
}
else if (item_size == ui.screensize && (item_flags & IF_RESIZABLE))
{
item_size = item_size*0.5;
draginfo[0] = draginfo[0]*0.5;
if (this.item_resized)
this.item_resized();
efsize = item_size;
}
else
{
item_position = ui.mousepos - (pos - item_position) - draginfo;
item_position[2] = 0;
if (item_flags & IF_RESIZABLE)
if (item_resized)
item_resized();
}
}
}
//bound it to the screen... just in case the screen got resized or something
if (item_position[0] > ui.screensize[0] - item_size[0]*0.25)
item_position[0] = ui.screensize[0] - item_size[0]*0.25;
if (item_position[1] > ui.screensize[1] - item_framesize[1])
item_position[1] = ui.screensize[1] - item_framesize[1];
if (item_position[0] < item_size[0]*-0.75)
item_position[0] = item_size[0]*-0.75;
if (item_position[1] < 0)
item_position[1] = 0;
local float drag = 0;
if (draginfo[2])
drag = draginfo[2];
else if (item_flags & IF_MFOCUSED)
drag = this::whichgrabhandle(pos);
if (drag)
this::drawgrabhandle(pos, drag);
ui.drawfill(pos, efsize, item_rgb, item_alpha, 0);
local vector col = '1 1 1';
if (item_flags & IF_KFOCUSED)
col_x = 0;
if ((item_flags & IF_MFOCUSED) && mouseinbox(pos, [efsize_x-item_framesize[1], item_framesize[1]]))
col_z = 0;
inset = (item_framesize[1]-item_scale)*0.5;
ui.drawstring(pos + '1 1'*inset, item_text, item_scale*'1 1', col, 1, 0);
if (!(item_flags & IF_NOKILL))
{
if (mouseinbox(pos + [efsize_x-item_framesize[1], 0], item_framesize[1]*'1 1'))
col = '1 1 0';
else
col = '1 1 1';
ui.drawstring(pos + [efsize_x+inset-item_framesize[1], inset], "X", item_scale*'1 1', col, 1, 0);
}
super::item_draw(pos);
};
mitem_menu(mitem_frame desktop, string mname, vector sz) menu_spawn =
{
mitem_menu n = spawn(mitem_menu, item_text:mname, item_framesize:'4 16 4', item_size:sz);
// n.item_flags |= IF_RESIZABLE;
if (desktop)
desktop.addr(n, RS_X_MIN_PARENT_MIN | RS_X_MAX_OWN_MIN | RS_Y_MIN_PARENT_MIN | RS_Y_MAX_OWN_MIN, (desktop.item_size - n.item_size) * 0.5, n.item_size);
return n;
};