Add mods menu and all-cvars menu to menusys.

combo widgets  now have sliders on dropdown lists.
text edit widgets accept mouse-clicks to move the cursor.
Fixed a bug with audio device selection.


git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@5671 fc73d0e0-1445-4013-8a0c-d673dee63da5
This commit is contained in:
Spoike 2020-04-19 03:56:01 +00:00
parent 50c4e75c65
commit 81b270db70
18 changed files with 1954 additions and 347 deletions

View file

@ -1126,6 +1126,9 @@ ADD_CUSTOM_TARGET(menusys ALL
quakec/menusys/menu/presets.qc
quakec/menusys/menu/servers.qc
quakec/menusys/menu/main.qc
quakec/menusys/menu/mods.qc
quakec/menusys/menu/cvars.qc
quakec/menusys/menu/updates.qc
quakec/menusys/menu/options_audio.qc
quakec/menusys/menu/options_configs.qc
quakec/menusys/menu/options_hud.qc

52
ftechrootbuild.sh Normal file
View file

@ -0,0 +1,52 @@
#!/bin/sh
#Script to set up a debian chroot suitable for compiling fte's public builds.
#deterministic builds are attempted but also requires:
# gcc/etc versions must match exactly (we use debian oldstable, which should reduce issues...)
# sourcecode must be unmodified, particuarly if 'svnversion' reports modified even in an irrelevant file then embedded revision numbers will be wrong.
# third party dependancies need to work and not get messed up (either me failing to re-run makelibs, random wget failures, or outdated revisions being removed from public access)
# obtained sourcecode revision must match the binary you're trying to duplicate (pre-5601 will insist on updating to latest svn (which may not even have a public build), so expect problems trying to duplicate older builds when the scripts instead try to grab the most recent build).
#for regular use you should probably set up schroot so you don't need to remember so many args
#requires about 2.3gb for the chroot+win64 build.
#android and emscripten targets require proper mounting of /proc and /dev and are NOT supported by this script. don't try enabling them
FTEBUILD=/tmp/ftebuild #change freely
CHUID=1000 #should generally be your non-root user id, giving you the same access in or out of the chroot...
CHUSER=spike #sadly this matters. youll just need to pretend to be me inside your chroot for now.
DEBIANMIRROR=http://ftp.uk.debian.org/debian/
DEBIANVERSION=stretch #oldstable now... should update to stable, but paranoid to update due to portability of glibc symbols.
LANG= #no language packs installed, so would be spammy if the following rules inherit the host lang
#FTEREVISON="-r 5601" #earlier than 5601 will fail (scripts will try to update to latest)
#THREADS="-j 8" #override number of threads to compile with, if you have a decent cpu.
#package lists
#general packages required to get the build system working (python+unzip+etc is for third-party dependancies)
GENERALPACKAGES= subversion build-essential automake ca-certificates unzip p7zip-full zip libtool python pkg-config
#package list needed to crosscompile for windows
WINTARGETPACKAGES= mingw-w64
#dev packages required to compile the linux build properly. Comment out for less downloads/diskusage
LINUXTARGETPACKAGES= gcc-multilib g++-multilib mesa-common-dev libasound2-dev libxcursor-dev libgnutls28-dev
#NOTE: chroot does NOT wipe all environment settings. some get carried over. This is a problem if your distro has a default PATH that excludes the system programs on debian, so this is included to be sure.
export PATH=/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games
#Set up our chroot (you can skip this part entirely if you're preconfigured a VM)
#make sure debootstrap is installed, without erroring out if you're not on debian-derivative (NOTE: such users will needs to manually install it first from somewhere!)
(which apt-get>/dev/null) && apt-get install --no-install-recommends debootstrap
#create the new debian chroot. it should receive the most recent versions of packages.
debootstrap $DEBIANVERSION $FTEBUILD $DEBIANMIRROR
echo "FTEBuild">$FTEBUILD/etc/debian_chroot #optional, so it shows if you decide to run a bash prompt inside the chroot.
chroot $FTEBUILD adduser --uid $CHUID $CHUSER #create a user (with a homedir), so we dont depend upon root inside the guest, where possible
#Install the extra packages needed to build
chroot $FTEBUILD apt-get install --no-install-recommends $GENERALPACKAGES $WINTARGETPACKAGES $LINUXTARGETPACKAGES
#NOW we finally start with non-debian downloads by grabbing the FTE sourcecode
chroot $FTEBUILD su $CHUSER -c "svn checkout https://svn.code.sf.net/p/fteqw/code/trunk ~/quake/fteqw-code $FTEREVISON" #grab all the source code.
#FTE has some setup bollocks, which does some toolchain checks and such. You can choose which targets to build here.
#NOTE: the following line will download third-party packages.
chroot $FTEBUILD su $CHUSER -c "cd ~/quake/fteqw-code && ./build_setup.sh --noupdate"
#And finally the main rebuild thing. drop the --noupdate part if you want to build the latest-available revision.
chroot $FTEBUILD su $CHUSER -c "cd ~/quake/fteqw-code && ./build_wip.sh --noupdate $THREADS"
#to remove your chroot afterwards:
#rm --one-file-system -rf $FTEBUILD

File diff suppressed because it is too large Load diff

View file

@ -48,6 +48,9 @@ void(mitem_desktop desktop) M_Pop =
cmd("m_load", M_Load, menu/loadsave.qc) \
cmd("m_save", M_Save, ) \
cmd("m_quit", M_Quit, menu/quit.qc) \
cmd("m_mods", M_Menu_Mods, menu/mods.qc) \
cmd("m_updates", M_Menu_Updates, menu/updates.qc) \
cmd("m_cvars", M_Menu_Cvars, menu/cvars.qc) \
cmd("m_newgame", M_NewGame, menu/newgame.qc) \
cmd("m_servers", M_Servers, menu/servers.qc) \
cmd("m_configs", M_Configs, menu/options_configs.qc) \
@ -71,7 +74,12 @@ void(mitem_desktop desktop) M_Pop =
mitem_desktop desktop;
void() m_shutdown = {};
void(vector screensize) m_draw = {items_draw(desktop);};
void(vector screensize) m_draw =
{
if (dp_workarounds)
cltime = gettime(0);
items_draw(desktop);
};
void(float scan, float chr) m_keydown = {items_keypress(desktop, scan, chr, TRUE);};
void(float scan, float chr) m_keyup = {items_keypress(desktop, scan, chr, FALSE);};
void(float mode) m_toggle
@ -90,6 +98,23 @@ void(float mode) m_toggle
items_updategrabs(TRUE);
};
float(string cstr) m_consolecommand =
{
tokenize(cstr);
string cmd = argv(0);
switch(cmd)
{
//switch on the known commands.
#define cmd(n,f) case n: f(desktop); break;
concommandslist
#undef cmd
default:
return FALSE;
}
items_updategrabs(TRUE);
return TRUE;
}
var float autocvar_dp_workarounds_allow = TRUE;
var float autocvar_dp_workarounds_force = FALSE;
@ -97,10 +122,19 @@ void() m_init =
{
desktop = spawn(mitem_desktop);
if (checkbuiltin(registercommand))
{
#define cmd(n,f) registercommand(n);
concommandslist
#undef cmd
}
else
{
//register the console commands via the alias command.
#define cmd(n,f) localcmd("alias " n " \"menu_cmd " n " $*\"\n");
concommandslist
#undef cmd
}
//work around some dp differences/bugs.
//this check identifies one significant bug in DP.

View file

@ -0,0 +1,265 @@
#include "../menusys/mitem_grid.qc"
class mitem_cvargrid : mitem_grid
{
strbuf grid_buf_names; //left column (
static void(strbuf newbuf) grid_setbuf =
{
if (grid_buf_names >= 0)
buf_del(grid_buf_names);
grid_buf_names = newbuf;
grid_numchildren = buf_getsize(grid_buf_names);
item_resized();
grid_kactive = grid_numchildren?0:-1;
grid_selectionchanged(-1,grid_kactive);
};
float cursorpos;
string newval;
static void() mitem_cvargrid =
{
grid_buf_names = -1;
cursorpos = -1;
};
static void(float idx) startedit =
{
if (cursorpos < 0)
{
string v = bufstr_get(grid_buf_names, idx);
float flags = cvar_type(v);
if (!(flags&1) || (flags&32)) //(!exists || readonly). we don't bother to check for private - we can still set them (they'll just read as empty).
return;
newval = strzone(cvar_string(v));
cursorpos = strlen(newval);
}
};
static string() getdesc =
{
float idx = grid_kactive;
if (idx < 0 || idx >= grid_numchildren)
return __NULL__;
string v = bufstr_get(grid_buf_names, idx);
return cvar_description(v);
};
virtual void(vector pos, float idx) grid_draw;
virtual float(vector pos, float scan, float char, float down, float idx) grid_keypress;
virtual void(float olditem, float newitem) grid_selectionchanged;
};
void(vector pos, float idx) mitem_cvargrid::grid_draw =
{
string text = bufstr_get(grid_buf_names, idx);
string value = cvar_string(text);
string defvalue = cvar_defstring(text);
float flags;
vector col = item_rgb;
if (item_flags & IF_MFOCUSED && idx == grid_mactive)
col_z = 0;
if (item_flags & IF_KFOCUSED && idx == grid_kactive)
col_x = 0;
flags = cvar_type(text);
if (!(flags&1))
value = "<INVALID>";
// if (flags & 2) //archive
// text = strcat("^hseta^h ", text);
if (flags & 4) //private
{
if (!value) //should always be true...
value = "???";
if (checkextension("FTE_EXTENDEDTEXTCODES"))
value = strcat("^&FE", value); //yellow, so you know its valid is meaningless.
}
//8 means engine, or something
//16 just means it has a description. who even cares?
if (flags & 32)
{
if (checkextension("FTE_EXTENDEDTEXTCODES"))
value = strcat("^&F4", value); //make it red so you know its pointless trying to change it.
}
if (idx == grid_kactive && cursorpos >= 0)
{ //we're editing...
value = newval;
if (value == defvalue)
col *= 0.5;
if (((cltime*4)&1) && (item_flags & IF_KFOCUSED))
value = strcat(substring(value, 0, cursorpos), chr2str(0xe00b), substring(value, cursorpos+1, -1)); //replace the char with a box... ugly, whatever
}
else if (value == defvalue)
col *= 0.5;
vector valuepos = [pos_x+item_size_x/2, pos_y];
pos_x = valuepos_x - stringwidth(text, TRUE, '1 1 0'*this.item_scale) - 8;
valuepos_x += 1;
ui.drawstring(pos, text, '1 1 0' * item_scale, col, item_alpha, 0);
ui.drawstring(valuepos, value, '1 1 0' * item_scale, col, item_alpha, 0);
};
void(float olditem, float newitem) mitem_cvargrid::grid_selectionchanged =
{
if (olditem == newitem)
return;
if (olditem && cursorpos >= 0)
{ //we were editing... change it now.
string v = bufstr_get(grid_buf_names, olditem);
cvar_set(v, newval);
}
cursorpos = -1; //stop editing now.
newval = "";
};
float(vector pos, float scan, float char, float down, float idx) mitem_cvargrid::grid_keypress =
{
if (!down)
return FALSE;
else if (scan == K_ESCAPE)
{
if (cursorpos >= 0)
cursorpos = -1; //cancel editing, forgetting the change.
else
return FALSE;
}
else if (scan == K_ENTER)
{
if (cursorpos >= 0)
grid_selectionchanged(idx, -1);
else
startedit(idx);
}
else if (scan == K_LEFTARROW && cursorpos>=0)
cursorpos = max(cursorpos-1, 0);
else if (scan == K_RIGHTARROW && cursorpos>=0)
cursorpos+=1;
else if (scan == K_MOUSE1)
{
startedit(idx);
if (cursorpos>=0)
{
float valuepos = pos_x+(item_size_x/2)+1;
cursorpos = strlen(newval);
if (ui.mousepos[0] > valuepos-8)
while (cursorpos>0 && ui.mousepos[0] < valuepos+stringwidth(substring(newval, 0, cursorpos), TRUE, '1 1 0'*item_scale))
cursorpos--;
}
}
else if (scan == K_DEL && cursorpos<0)
{ //reset to default
string v = bufstr_get(grid_buf_names, idx);
cvar_set(v, cvar_defstring(v));
}
else if (scan == K_HOME && cursorpos>= 0)
cursorpos = 0;
else if (scan == K_END && cursorpos>= 0)
cursorpos = strlen(newval);
else if (scan == K_DEL && cursorpos>=0 && cursorpos<strlen(newval))
newval = strzone(strcat(substring(newval, 0, cursorpos), substring(newval, cursorpos+1, -1)));
else if ((scan == K_BACKSPACE || scan == K_DEL) && cursorpos>=0)
{
if (cursorpos>0)
{
newval = strzone(strcat(substring(newval, 0, cursorpos-1), substring(newval, cursorpos, -1)));
cursorpos -= 1;
}
}
else if (char >= ' ' && cursorpos>=0)
{
string ins = chr2str(char);
newval = strzone(strcat(substring(newval, 0, cursorpos), ins, substring(newval, cursorpos, -1)));
cursorpos += strlen(ins);
}
else
return FALSE;
return TRUE;
};
class cvarsmenu : mitem_exmenu
{
string filter;
float modifiedonly;
mitem_cvargrid grid;
static void(void) updatefilters =
{
strbuf b = buf_create();
buf_cvarlist(b, filter, "_*");
if (modifiedonly)
{
for (float i = buf_getsize(b); i --> 0; )
{
string v = bufstr_get(b, i);
if (cvar_string(v) == cvar_defstring(v))
bufstr_free(b, i);
else if (cvar_type(v)&32)
bufstr_free(b, i); //also remove read-only cvars. we can't reset them or anything so there's no point listing them here.
}
buf_sort(b, 0, 0); //sort the names, to clean up any gaps
}
grid.grid_setbuf(b);
};
virtual string(string key) get =
{
if (key == "*filter")
return filter;
if (key == "*modified")
return ftos(modifiedonly);
if (key == "*desc")
return grid.getdesc();
return super::get(key);
};
virtual void(string key, string newval) set =
{
if (key == "*filter")
filter = newval;
else if (key == "*modified")
modifiedonly = stof(newval);
else
return super::set(key, newval);
updatefilters();
};
virtual float(string key) isvalid =
{
if (key == "*filter")
return TRUE;
if (key == "*modified")
return TRUE;
return super::isvalid(key);
};
};
void(mitem_desktop desktop) M_Menu_Cvars =
{
mitem it;
float h = (480+240)/2;
//create the menu, give it focus, and make sure its displayed over everything else.
cvarsmenu m = spawn(cvarsmenu, item_text:_("Mods List"), item_flags:IF_SELECTABLE, item_command:"m_main", frame_stdheight:h);
desktop.add(m, RS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MIN | RS_X_MAX_PARENT_MAX|RS_Y_MAX_PARENT_MAX, '0 0', '0 0');
desktop.item_focuschange(m, IF_KFOCUSED);
m.totop();
//draw title art above the options
mitem_pic banner = spawn(mitem_pic, item_text:"gfx/ttl_cstm.lmp", item_size_y:24, item_flags:IF_CENTERALIGN);
m.add(banner, RS_X_MIN_PARENT_MID|RS_Y_MIN_PARENT_MID | RS_X_MAX_PARENT_MID|RS_Y_MAX_OWN_MIN, [banner.item_size_x*-0.5, h*-0.5], [banner.item_size_x*0.5, 24]);
it = menuitemeditt_spawn("Cvar Filter", "*filter", '280 8');
m.add(it, RS_X_MIN_PARENT_MID|RS_Y_MIN_PARENT_MID | RS_X_MAX_PARENT_MID|RS_Y_MAX_OWN_MIN, [-160, h*-0.5+32], [40, 8]);
it = menuitemcheck_spawn("Modified Only", "*modified", '280 8');
m.add(it, RS_X_MIN_PARENT_MID|RS_Y_MIN_PARENT_MID | RS_X_MAX_PARENT_MID|RS_Y_MAX_OWN_MIN, [40, h*-0.5+32], [200, 8]);
//spawn our grid for the actual options. this provides a scrollbar if we have too many items.
m.grid = spawn(mitem_cvargrid, item_flags: IF_SELECTABLE, item_scale: 8);
m.add(m.grid, RS_X_MIN_PARENT_MID|RS_Y_MIN_PARENT_MID | RS_X_MAX_OWN_MIN|RS_Y_MAX_PARENT_MID, [-160, h*-0.5+48], [320, h*0.5-64]);
it = spawn(mitem_label, item_text: "*desc", item_flags: 0, item_scale: 8);
m.add(it, RS_X_MIN_PARENT_MID|RS_Y_MIN_PARENT_MID | RS_X_MAX_OWN_MIN|RS_Y_MAX_PARENT_MID, [-160, h*0.5-60], [320, h*0.5]);
m.updatefilters();
//and give us a suitable menu tint too, just because.
addmenuback(m);
};

View file

@ -39,6 +39,12 @@ float(string cmd) assumefalsecheckcommand =
return checkcommand(cmd);
};
float(__variant cmd, float assumption) checkbuiltin2 =
{
if (!checkbuiltin(checkbuiltin)) //teehee
return assumption;
return checkbuiltin(cmd);
};
nonstatic void(mitem_desktop desktop) M_Main =
@ -88,8 +94,10 @@ nonstatic void(mitem_desktop desktop) M_Main =
if (assumefalsecheckcommand("cef")) {menuitemtext_cladd16(m, _("Browser"), "m_pop;cef google.com", y); y += 16;}
if (assumefalsecheckcommand("xmpp")) {menuitemtext_cladd16(m, _("Social"), "m_pop;xmpp", y); y += 16;}
if (assumefalsecheckcommand("irc")) {menuitemtext_cladd16(m, _("IRC"), "m_pop;irc /info", y); y += 16;}
if (assumefalsecheckcommand("menu_download")) {menuitemtext_cladd16(m, _("Updates+Packages"), "m_pop;menu_download", y); y += 16;}
/*if (checkbuiltin2(getpackagemanagerinfo,FALSE)) {menuitemtext_cladd16(m, _("Updates+Packages"), "m_pop;m_updates", y); y += 16;}
else*/ if (assumefalsecheckcommand("menu_download")) {menuitemtext_cladd16(m, _("Updates+Packages"), "m_pop;menu_download", y); y += 16;}
if (assumefalsecheckcommand("qi")) {menuitemtext_cladd16(m, _("Quake Injector"), "m_pop;qi", y); y += 16;}
if (checkbuiltin2(getgamedirinfo,TRUE)) {menuitemtext_cladd16(m, _("Mods"), "m_pop;m_mods", y); y += 16;}
{menuitemtext_cladd16(m, _("Options"), "m_pop;m_options", y); y += 16;}
{menuitemtext_cladd16(m, _("Quit"), "m_pop;m_quit", y); y += 16;}

View file

@ -0,0 +1,77 @@
class mitem_pictext : mitem_text
{
string pic;
virtual void(vector pos) item_draw =
{
if (pic)
drawpic(pos, pic, [item_size_y,item_size_y], '1 1 1', 1, 0);
pos_x += item_size_y;
super::item_draw(pos);
};
};
void(mitem_desktop desktop) M_Menu_Mods =
{
float h = (480+240)/2;
//create the menu, give it focus, and make sure its displayed over everything else.
mitem_exmenu m = spawn(mitem_exmenu, item_text:_("Mods List"), item_flags:IF_SELECTABLE, item_command:"m_main", frame_stdheight:h);
desktop.add(m, RS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MIN | RS_X_MAX_PARENT_MAX|RS_Y_MAX_PARENT_MAX, '0 0', '0 0');
desktop.item_focuschange(m, IF_KFOCUSED);
m.totop();
//draw title art above the options
mitem_pic banner = spawn(mitem_pic, item_text:"gfx/ttl_cstm.lmp", item_size_y:24, item_flags:IF_CENTERALIGN);
m.add(banner, RS_X_MIN_PARENT_MID|RS_Y_MIN_PARENT_MID | RS_X_MAX_PARENT_MID|RS_Y_MAX_OWN_MIN, [banner.item_size_x*-0.5, h*-0.5], [banner.item_size_x*0.5, 24]);
//spawn a container frame for the actual options. this provides a scrollbar if we have too many items.
mitem_frame fr = spawn(mitem_frame, item_flags: IF_SELECTABLE, frame_hasscroll:TRUE);
m.add(fr, RS_X_MIN_PARENT_MID|RS_Y_MIN_PARENT_MID | RS_X_MAX_OWN_MIN|RS_Y_MAX_PARENT_MID, [-140, h*-0.5+32], [280, h*0.5]);
float mod;
for (mod = 0; ; mod++)
{
string gamedir = getgamedirinfo(mod, GGDI_GAMEDIR);
if not(gamedir)
break;
string desc = getgamedirinfo(mod, GGDI_DESCRIPTION);
if (desc=="")
{ //clean up some stuff a little, if we know it.
string lwr = strtolower(gamedir); //windows sucks.
if (lwr == "hipnotic")
desc = "Scourge of Armagon";
else if (lwr == "rogue")
desc = "Dissolution of Eternity";
else if (lwr == "dopa")
desc = "Dimension of the Past";
else if (lwr == "ad")
desc = "Arcane Dimensions";
else if (lwr == "quoth")
desc = "Quoth";
else if (lwr == "rally")
desc = "Quake Rally";
else if (lwr == "fortress")
desc = "Team Fortress";
}
if (desc=="")
desc = sprintf("%s/", gamedir); //no description given, so make it clear that its an actual subdir name
else
desc = sprintf("%s ^h(%s)", desc, gamedir); //include the gamedir, faded somewhat.
string cmd = getgamedirinfo(mod, GGDI_LOADCOMMAND);
if not(cmd) //for dp users, if they somehow run this
cmd = sprintf("gamedir %s", gamedir);
string icon = getgamedirinfo(mod, GGDI_ICON);
//add some extra stuff to reset the menu
cmd = strcat("m_pop; ", cmd, "; togglemenu");
fr.add(spawn(mitem_pictext, pic:icon, item_text:desc, item_command:cmd, item_scale:16, item_flags:0), RS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MIN | RS_X_MAX_PARENT_MAX|RS_Y_MAX_OWN_MIN, [0, mod*16], [0, 16]);
}
if (!mod)
fr.add(spawn(mitem_text, item_text:"No mods detected", item_scale:16, item_flags:0), RS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MIN | RS_X_MAX_PARENT_MAX|RS_Y_MAX_OWN_MIN, [0, mod*16], [0, 16]);
//and give us a suitable menu tint too, just because.
addmenuback(m);
};

View file

@ -38,6 +38,7 @@ nonstatic void(mitem_desktop desktop) M_Options =
{fr.add(spawn(mitem_text, item_text:"Graphical Presets", item_command:"m_pop;m_preset", item_scale:16, item_flags:IF_CENTERALIGN), fl, [0, pos], [0, 16]); pos += 16;}
fr.add(spawn(mitem_text, item_text:"Game Configs", item_command:"m_pop;m_configs", item_scale:16, item_flags:IF_CENTERALIGN), fl, [0, pos], [0, 16]); pos += 16;
fr.add(spawn(mitem_text, item_text:"Basic Setup", item_command:"m_pop;m_basicopts", item_scale:16, item_flags:IF_CENTERALIGN), fl, [0, pos], [0, 16]); pos += 16;
fr.add(spawn(mitem_text, item_text:"Keys", item_command:"m_pop;m_keys", item_scale:16, item_flags:IF_CENTERALIGN), fl, [0, pos], [0, 16]); pos += 16;
fr.add(spawn(mitem_text, item_text:"Audio", item_command:"m_pop;m_audio", item_scale:16, item_flags:IF_CENTERALIGN), fl, [0, pos], [0, 16]); pos += 16;
fr.add(spawn(mitem_text, item_text:"Video", item_command:"m_pop;m_video", item_scale:16, item_flags:IF_CENTERALIGN), fl, [0, pos], [0, 16]); pos += 16;
fr.add(spawn(mitem_text, item_text:"Effects", item_command:"m_pop;m_effects", item_scale:16, item_flags:IF_CENTERALIGN), fl, [0, pos], [0, 16]); pos += 16;
@ -45,11 +46,12 @@ nonstatic void(mitem_desktop desktop) M_Options =
{fr.add(spawn(mitem_text, item_text:"Particles", item_command:"m_pop;m_particles", item_scale:16, item_flags:IF_CENTERALIGN), fl, [0, pos], [0, 16]); pos += 16;}
if (assumefalsecheckcommand("ezhud_nquake"))
{fr.add(spawn(mitem_text, item_text:"Hud", item_command:"m_pop;m_hud", item_scale:16, item_flags:IF_CENTERALIGN), fl, [0, pos], [0, 16]); pos += 16;}
fr.add(spawn(mitem_text, item_text:"Keys", item_command:"m_pop;m_keys", item_scale:16, item_flags:IF_CENTERALIGN), fl, [0, pos], [0, 16]); pos += 16;
if (assumefalsecheckcommand("cfg_save"))
{fr.add(spawn(mitem_text, item_text:"Save Settings", item_command:"cfg_save", item_scale:16, item_flags:IF_CENTERALIGN), fl, [0, pos], [0, 16]); pos += 16;}
if (checkbuiltin2(buf_cvarlist, TRUE))
{fr.add(spawn(mitem_text, item_text:"Advanced Guru Settings", item_command:"m_pop;m_cvars", item_scale:16, item_flags:IF_CENTERALIGN), fl, [0, pos], [0, 16]); pos += 16;}
if (assumefalsecheckcommand("cvarreset"))
{fr.add(spawn(mitem_text, item_text:"Reset to Defaults", item_command:"m_reset", item_scale:16, item_flags:IF_CENTERALIGN), fl, [0, pos], [0, 16]); pos += 16;}
if (assumefalsecheckcommand("cfg_save"))
{fr.add(spawn(mitem_text, item_text:"Save Settings", item_command:"cfg_save", item_scale:16, item_flags:IF_CENTERALIGN), fl, [0, pos], [0, 16]); pos += 16;}
//random art for style
#if 1//def CSQC

View file

@ -1,8 +1,30 @@
static class audiomenu : mitem_exmenu
{
virtual string(string key) get =
{
if (key == "s_device" || key == "cl_voip_capturedevice")
{ //the cvar supports multiple options, but we only support one. :(
//so just return the first to avoid getting too confused.
tokenize(super::get(key));
return argv(0);
}
return super::get(key);
};
virtual void(string key, string val) set =
{
if (key == "s_device" || key == "cl_voip_capturedevice")
{ //add some quotes.
val = strcat("\"", val, "\"");
}
super::set(key, val);
};
};
nonstatic void(mitem_desktop desktop) M_Options_Audio =
{
local float pos;
mitem_exmenu m;
m = spawn(mitem_exmenu, item_text:_("Audio Options"), item_flags:IF_SELECTABLE, item_command:"m_options");
m = spawn(audiomenu, item_text:_("Audio Options"), item_flags:IF_SELECTABLE, item_command:"m_options");
desktop.add(m, RS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MIN | RS_X_MAX_PARENT_MAX|RS_Y_MAX_PARENT_MAX, '0 0', '0 0');
desktop.item_focuschange(m, IF_KFOCUSED);
m.totop();
@ -29,12 +51,14 @@ nonstatic void(mitem_desktop desktop) M_Options_Audio =
fr.add(menuitemslider_spawn(_("Music Volume"), "bgmvolume", '0 0.5 0.05', '280 8'), fl, [0, pos], [0, 8]); pos += 8;
fr.add(menuitemslider_spawn(_("Channels"), "s_numspeakers", '1 6 1', '280 8'), fl, [0, pos], [0, 8]); pos += 8;
fr.add(menuitemcombo_spawn(_("Audio Quality"), "s_khz", '280 8', _(
"11025 \"11025hz (vanilla quake)\" "
"22050 \"22050hz\" "
"44100 \"44100hz (cd quality)\" "
"48000 \"48000hz (dvd quality)\" "
"96000 \"96000hz\" "
"192000 \"192000hz\" "
"8 \"8000hz (telephone quality)\" "
"11.025 \"11025hz (vanilla quake)\" "
"22.05 \"22050hz\" "
"44.1 \"44100hz (cd quality)\" "
"48 \"48000hz (dvd quality)\" "
//higher values are probably pointless when source data doesn't go that high, so not going to list them.
//"96 \"96000hz (blu-ray quality)\" "
//"192 \"192000hz (professional quality)\" "
)), fl, [0, pos], [0, 8]); pos += 8;
fr.add(menuitemcheck_spawn(_("Doppler"), "s_doppler", '280 8'), fl, [0, pos], [0, 8]); pos += 8;
fr.add(menuitemcheck_spawn(_("8bit audio"), "s_loadas8bit", '280 8'), fl, [0, pos], [0, 8]); pos += 8;

View file

@ -17,7 +17,7 @@ nonstatic void(mitem_desktop desktop) M_Options_Hud =
//spawn a container frame for the actual options. this provides a scrollbar if we have too many items.
mitem_frame fr = spawn(mitem_frame, item_flags: IF_SELECTABLE, frame_hasscroll:TRUE);
m.add(fr, RS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MID | RS_X_MAX_PARENT_MAX|RS_Y_MAX_OWN_MIN, [0, -h], [0, h*2]);
m.add(fr, RS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MID | RS_X_MAX_PARENT_MAX|RS_Y_MAX_OWN_MIN, [0, -h+16], [0, h*2+32]);
float fs = search_begin("huds/*.cfg", TRUE, TRUE);
float fc = search_getsize(fs);

View file

@ -0,0 +1,184 @@
#include "../menusys/mitem_grid.qc"
enum
{
GPMI_NAME, //name of the package, for use with the pkg command.
GPMI_CATEGORY, //category text
GPMI_TITLE, //name of the package, for showing the user.
GPMI_VERSION, //version info (may have multiple with the same name but different versions)
GPMI_DESCRIPTION, //some blurb
GPMI_LICENSE, //what license its distributed under
GPMI_AUTHOR, //name of the person(s) who created it
GPMI_WEBSITE, //where to contribute/find out more info/etc
GPMI_INSTALLED, //current state
GPMI_ACTION, //desired state
GPMI_AVAILABLE, //whether it may be downloaded or not.
GPMI_FILESIZE, //whether it may be downloaded or not.
};
class mitem_updategrid : mitem_grid
{
virtual void(vector pos) item_draw =
{ //make sure we see any updates as they're detected...
if (getpackagemanagerinfo(grid_numchildren, GPMI_NAME))
{
grid_numchildren++;
while(getpackagemanagerinfo(grid_numchildren, GPMI_NAME))
grid_numchildren++;
item_resized();
}
super::item_draw(pos);
};
virtual void(vector pos, float idx) grid_draw;
virtual float(vector pos, float scan, float char, float down, float idx) grid_keypress;
// virtual void(float olditem, float newitem) grid_selectionchanged;
};
void(vector pos, float idx) mitem_updategrid::grid_draw =
{
string text = getpackagemanagerinfo(idx, GPMI_TITLE);
vector col = item_rgb;
if (item_flags & IF_MFOCUSED && idx == grid_mactive)
col_z = 0;
if (item_flags & IF_KFOCUSED && idx == grid_kactive)
col_x = 0;
string status = getpackagemanagerinfo(idx, GPMI_INSTALLED);
string action = getpackagemanagerinfo(idx, GPMI_ACTION);
if (action == "purge") //delete the thing
action = "^&F4 ";
else if (action=="reinstall") //purge then reinstall(the purge status was explicit at least.)
action = "^&F2P";
else if (action=="user") //install(explcitly by user)
action = "^&F2 ";
else if (action=="auto") //install(to satisfy dependancies)
action = "^&FA ";
else if (action=="disable")
action = "^&FE ";
else if (action=="retain")
action = "^&FE ";
else
action = "^&F4 ";
if (status == "0%" || status == "pending")
{
ui.drawfill(pos, [item_size_x, item_scale], '0 0 0.5', 0.1, 0);
action = status = " ";
}
else if (strstrofs(status, "%")>=0)
{
float frac = stof(status)/100;
ui.drawfill(pos, [item_size_x*frac, item_scale], '0 0 0.5', 0.9, 0);
action = status = " ";
}
else if (status == "corrupt")
status = "^&FE?"; //yellow
else if (status == "enabled")
status = "^&F2 "; //green
else if (status == "present")
status = "^&FE "; //yellow
else
status = "^&F4 "; //red
ui.drawstring(pos, strcat(status, action, "^&F-", text), '1 1 0' * item_scale, col, item_alpha, 0);
};
/*void(float olditem, float newitem) mitem_updategrid::grid_selectionchanged =
{
};
*/float(vector pos, float scan, float char, float down, float idx) mitem_updategrid::grid_keypress =
{
string text;
if (!down)
return FALSE;
else if (scan == K_DEL||scan == K_BACKSPACE||scan == K_LEFTARROW)
{
text = getpackagemanagerinfo(idx, GPMI_NAME);
if (!text)
return FALSE;
string action = getpackagemanagerinfo(idx, GPMI_ACTION);
if (action == "purge") //delete the thing
action = "rem";
else if (action=="reinstall") //purge then reinstall(the purge status was explicit at least.)
action = "rem";
else if (action=="user") //install(explcitly by user)
action = "rem";
else if (action=="auto") //install(to satisfy dependancies)
action = "rem";
else
action = "del";
text = strcat("pkg quiet_", action, " \"", text, "\"\n");
localcmd(text);
}
else if (scan == K_RIGHTARROW || scan==K_ENTER || scan == K_MOUSE1)
{
text = getpackagemanagerinfo(idx, GPMI_NAME);
if (!text)
return FALSE;
text = strcat("pkg quiet_add \"", text, "\"\n");
localcmd(text);
}
else
return FALSE;
return TRUE;
};
class menu_updates : mitem_exmenu
{
string filter;
mitem_updategrid grid;
virtual string(string key) get =
{
if (key == "info")
{
string text, tmp;
tmp = getpackagemanagerinfo(grid.grid_kactive, GPMI_VERSION); text = strcat(text, "^aVersion:^a ", tmp?:"Unspecified");
tmp = getpackagemanagerinfo(grid.grid_kactive, GPMI_AUTHOR); text = strcat(text, "\n^aAuthor:^a ", tmp?:"Unknown");
tmp = getpackagemanagerinfo(grid.grid_kactive, GPMI_LICENSE); text = strcat(text, "\n^aLicense:^a ", tmp?:"Unknown");
tmp = getpackagemanagerinfo(grid.grid_kactive, GPMI_WEBSITE); text = strcat(text, "\n^aWebsite:^a ", tmp?:"Unknown");
tmp = getpackagemanagerinfo(grid.grid_kactive, GPMI_DESCRIPTION); text = strcat(text, "\n^aDescription:^a ", tmp?:"No description specified");
return text;
}
return super::get(key);
};
};
void(mitem_desktop desktop) M_Menu_Updates =
{
mitem it;
float h = (480+240)/2;
localcmd("pkg update\n");
//create the menu, give it focus, and make sure its displayed over everything else.
menu_updates m = spawn(menu_updates, item_text:_("Updates List"), item_flags:IF_SELECTABLE, item_command:"m_main", frame_stdheight:h);
desktop.add(m, RS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MIN | RS_X_MAX_PARENT_MAX|RS_Y_MAX_PARENT_MAX, '0 0', '0 0');
desktop.item_focuschange(m, IF_KFOCUSED);
m.totop();
//draw title art above the options
mitem_pic banner = spawn(mitem_pic, item_text:"gfx/ttl_cstm.lmp", item_size_y:24, item_flags:IF_CENTERALIGN);
m.add(banner, RS_X_MIN_PARENT_MID|RS_Y_MIN_PARENT_MID | RS_X_MAX_PARENT_MID|RS_Y_MAX_OWN_MIN, [banner.item_size_x*-0.5, h*-0.5], [banner.item_size_x*0.5, 24]);
//spawn our grid for the actual options. this provides a scrollbar if we have too many items.
m.grid = spawn(mitem_updategrid, item_flags: IF_SELECTABLE, item_scale: 8);
m.add(m.grid, RS_X_MIN_PARENT_MID|RS_Y_MIN_PARENT_MID | RS_X_MAX_PARENT_MID|RS_Y_MAX_PARENT_MID, [-160, h*-0.5+32], [40, h*0.5]);
it = spawn(mitem_label, item_text: "info", item_flags: 0, item_scale: 8);
m.add(it, RS_X_MIN_PARENT_MID|RS_Y_MIN_PARENT_MID | RS_X_MAX_PARENT_MAX|RS_Y_MAX_PARENT_MID, [40, h*-0.5+32], [0, h*0.5-16]);
it = spawn(mitem_button, item_text: "Apply", item_command: "pkg apply\n", item_flags: IF_SELECTABLE, item_scale: 8);
m.add(it, RS_X_MIN_PARENT_MID|RS_Y_MIN_PARENT_MID | RS_X_MAX_PARENT_MAX|RS_Y_MAX_PARENT_MID, [40+1, h*0.5-16+1], [0-1, h*0.5-1]);
//and give us a suitable menu tint too, just because.
addmenuback(m);
};

View file

@ -11,13 +11,13 @@ The possible values is a separate popup.
class mitem_combo;
class mitem_combo_popup;
class mitem_combo : mitem
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 cfriend;
mitem_combo_popup cpopup;
string mstrlist;
float firstrow;
float visrows;
@ -32,6 +32,10 @@ class mitem_combo : mitem
item_flags &= ~IF_SELECTABLE;
super::item_resized();
};
void() mitem_combo =
{
spos = -1;
};
};
class mitem_combo_popup : mitem
@ -40,22 +44,28 @@ class mitem_combo_popup : mitem
virtual float(vector pos, float scan, float char, float down) item_keypress;
virtual void(mitem newfocus, float changedflag) item_focuschange;
mitem_combo pfriend;
mitem_combo pcombo;
mitem_vslider pslider;
float slidercache;
virtual void() item_remove =
{
if (pfriend)
pfriend.cfriend = 0;
if (pslider)
pslider.item_remove();
if (pcombo)
pcombo.cpopup = 0;
super::item_remove();
};
};
void() mitem_combo::item_remove =
{
mitem_combo_popup p = cfriend;
mitem_combo_popup p = cpopup;
if (p)
p.item_remove();
strunzone(mstrlist);
item_text = __NULL__;
item_command = __NULL__;
super::item_remove();
};
@ -67,10 +77,16 @@ void(vector pos) mitem_combo::item_draw =
if (!(item_flags & IF_SELECTABLE))
rgb *= 0.2;
if (cfriend)
cfriend.item_position = pos + [item_size_x / 2, item_size_y];
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);
@ -105,17 +121,18 @@ void(vector pos) mitem_combo::item_draw =
};
void(mitem newfocus, float flag) mitem_combo::item_focuschange =
{
if (!cfriend || !(flag & IF_KFOCUSED))
if (!cpopup || !(flag & IF_KFOCUSED))
return; //don't care
if (newfocus != (mitem)this && newfocus != (mitem)cfriend)
if (newfocus != (mitem)this && newfocus != (mitem)cpopup)
{
cfriend.item_size = cfriend.maxs = '0 0';
cfriend.item_flags &~= IF_SELECTABLE;
cpopup.item_size = cpopup.maxs = '0 0';
cpopup.item_flags &~= IF_SELECTABLE;
}
spos = -1;
};
void(mitem newfocus, float flag) mitem_combo_popup::item_focuschange =
{
pfriend.item_focuschange(newfocus, flag);
pcombo.item_focuschange(newfocus, flag);
};
void(vector pos) mitem_combo_popup::item_draw =
{
@ -123,11 +140,15 @@ void(vector pos) mitem_combo_popup::item_draw =
if (item_size_y < 1)
return;
local mitem_combo f = pfriend;
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';
@ -135,13 +156,13 @@ void(vector pos) mitem_combo_popup::item_draw =
return;
}
ui.drawfill(pos, item_size, item_rgb, item_alpha, 0);
ui.drawfill(pos, popupsize, item_rgb, item_alpha, 0);
/* //border
ui.drawfill(pos, [item_size_x, 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-1, 1], [1, item_size_y - 1], TD_LFT, item_alpha, 0);
ui.drawfill(pos + [0, item_size_y-1], [item_size_x, 1], TD_TOP, item_alpha, 0);
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);
@ -149,13 +170,13 @@ void(vector pos) mitem_combo_popup::item_draw =
if (argv(c) == curval)
break;
if (c >= v)
c = 0;
c = -1;
i = f.firstrow;
i = i*2;
if (!f.visrows)
i = 0;
else
else if (c >= 0)
{
//bound the displayed position
if (c < i)
@ -163,17 +184,52 @@ void(vector pos) mitem_combo_popup::item_draw =
if (i < c - (f.visrows-1)*2)
i = c - (f.visrows-1)*2;
}
m = i + f.visrows*2;
f.firstrow = floor(i*0.5);
//constrain the drawing so it doesn't overflow the combo
ui.setcliparea(pos[0], pos[1], item_size[0], item_size[1]);
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, [item_size_x, item_scale]))
if (mouseinbox(pos, [popupsize_x, item_scale]))
col_z = 0;
if (c == i)
col_x = 0;
@ -187,7 +243,14 @@ void(vector pos) mitem_combo_popup::item_draw =
};
float(vector pos, float scan, float char, float down) mitem_combo_popup::item_keypress =
{
return pfriend.item_keypress(pos - [0, pfriend.item_size_y], scan, char, down);
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 =
{
@ -210,21 +273,21 @@ float(vector pos, float scan, float char, float down) mitem_combo::item_keypress
if (scan == K_ESCAPE || scan == K_MOUSE2)
{
if (cfriend)
if (cpopup)
{
cfriend.item_remove();
cpopup.item_remove();
return TRUE;
}
return FALSE;
}
else if (scan == K_MWHEELUP || (scan == K_UPARROW && cfriend))
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 && cfriend))
else if (scan == K_MWHEELDOWN || (scan == K_DOWNARROW && cpopup))
{
i += 2;
if (i >= c)
@ -233,20 +296,20 @@ float(vector pos, float scan, float char, float down) mitem_combo::item_keypress
}
else if (scan == K_MOUSE1 || scan == K_ENTER)
{
if (scan == K_ENTER && cfriend)
if (scan == K_ENTER && cpopup)
{
cfriend.item_remove();
cpopup.item_remove();
return TRUE;
}
visrows = ((c>18)?18/2:c/2);
if (!cfriend)
if (!cpopup)
{
cfriend = spawn(mitem_combo_popup);
cfriend.pfriend = this;
cfriend.item_scale = 8;
cfriend.item_rgb = MENUBACK_RGB;
cfriend.item_alpha = MENUBACK_ALPHA;
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)
@ -254,36 +317,44 @@ float(vector pos, float scan, float char, float down) mitem_combo::item_keypress
pos += fr.item_position;
fr = fr.item_parent;
}
fr.addr(cfriend, 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]);
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]);
}
cfriend.item_size = cfriend.maxs = [item_size_x*0.5, item_size_y*visrows];
cfriend.item_flags |= IF_SELECTABLE;
cfriend.totop();
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 && (cfriend.item_flags & IF_MFOCUSED))
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 /= cfriend.item_scale;
f /= cpopup.item_scale;
f += firstrow;
i = floor(f) * 2;
if (i < c)
{
curval = argv(i);
cfriend.item_flags &~= IF_SELECTABLE;
cfriend.item_size = cfriend.maxs = '0 0';
cpopup.item_flags &~= IF_SELECTABLE;
cpopup.item_size = cpopup.maxs = '0 0';
item_parent.item_focuschange(this, IF_MFOCUSED|IF_KFOCUSED);
cfriend.item_remove();
cpopup.item_remove();
}
}
}
else if (scan == K_BACKSPACE || scan == K_DEL)
curval = substring(curval, 0, -2);
else if (char >= ' ')
curval = strcat(curval, chr2str(char));
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;

View file

@ -321,10 +321,11 @@ void(mitem_desktop desktop) items_draw =
items_updategrabs(FALSE);
if (dp_workarounds && oldgrabstate)
{
if (drawgetimagesize(autocvar_cl_cursor) == '0 0')
// //hopefully dp isn't broken and reports non-zero sizes for files that failed.
// if (drawgetimagesize(autocvar_cl_cursor) == '0 0')
// ui.drawpic(ui.mousepos - autocvar_cl_cursorbias*'1 1', autocvar_cl_cursor, autocvar_cl_cursorsize*'1 1', '1 1 1', 1, 0);
// else
ui.drawcharacter(ui.mousepos - [stringwidth("+", TRUE, '4 4')*0.5, 4], '+', '8 8', '1 1 1', 1, 0);
else
ui.drawpic(ui.mousepos - autocvar_cl_cursorbias*'1 1', autocvar_cl_cursor, autocvar_cl_cursorsize*'1 1', '1 1 1', 1, 0);
}
#ifndef MENU

View file

@ -21,7 +21,9 @@ class mitem_edit : mitem
void() mitem_edit::item_remove =
{
if (item_text)
strunzone(item_text);
if (item_command)
strunzone(item_command);
super::item_remove();
};
@ -59,11 +61,20 @@ float(vector pos, float scan, float char, float down) mitem_edit::item_keypress
spos = max(spos-1, 0);
else if (scan == K_RIGHTARROW)
spos+=1;
/* else if (scan == K_MOUSE1)
else if (scan == K_HOME)
spos = 0;
else if (scan == K_END)
spos = strlen(curval);
else if (scan == K_MOUSE1)
{
//FIXME: figure out the spos for the cursor
return TRUE;
}*/
float valuepos = pos_x+(item_size_x/2)+1;
if (ui.mousepos[0] > valuepos-8)
{
spos = strlen(curval);
while (spos>0 && ui.mousepos[0] < valuepos+stringwidth(substring(curval, 0, spos), TRUE, '1 1 0'*item_scale))
spos--;
}
}
else if (scan == K_BACKSPACE || scan == K_DEL)
{
if (spos)
@ -89,7 +100,7 @@ mitem_edit(string text, string command, vector sz) menuitemeditt_spawn =
n.item_scale = sz_y;
n.item_text = strzone(text);
n.item_size = sz;
n.spos = 100000; //will be clipped so meh
n.spos = 10000000; //will be clipped so meh
n.item_command = strzone(command);
n.item_flags |= IF_SELECTABLE;

View file

@ -33,6 +33,7 @@ class mitem_frame : mitem
mitem_vslider vslider; //displayed if any client item's max[y] > our item_size[y]
vector item_framesize; //x=sides, y=top, z=bottom
float frame_stdheight; //adjust RS_Y_MIN_PARENT_MID to pinch inwards when the frame is smaller than this. Child items crossing the mid-point must size gracefully (like subframes).
float frame_hasscroll;
static mitem(string title) findchildtext;
@ -49,6 +50,71 @@ class mitem_frame : mitem
};
};
void(mitem ch, vector parentsize) mitem_parentresized =
{
float f = ch.resizeflags;
if (f & RS_X_FRACTION)
ch.item_position[0] = parentsize[0] * ch.mins[0];
else if (f & RS_X_MIN_PARENT_MAX)
ch.item_position[0] = parentsize[0] + ch.mins[0];
else if (f & RS_X_MIN_PARENT_MID)
ch.item_position[0] = parentsize[0]/2 + ch.mins[0];
else //if (f & RS_X_MIN_PARENT_MIN)
ch.item_position[0] = ch.mins[0];
if (f & RS_X_FRACTION)
ch.item_position[0] = parentsize[0] * ch.maxs[0];
else if (f & RS_X_MAX_PARENT_MIN)
ch.item_size[0] = ch.maxs[0] - ch.item_position[0];
else if (f & RS_X_MAX_PARENT_MID)
ch.item_size[0] = ch.maxs[0]+parentsize[0]/2 - ch.item_position[0];
else if (f & RS_X_MAX_PARENT_MAX)
ch.item_size[0] = ch.maxs[0]+parentsize[0] - ch.item_position[0];
else //if (f & RS_X_MAX_OWN_MIN)
ch.item_size[0] = ch.maxs[0];
if (f & RS_Y_FRACTION)
ch.item_position[1] = parentsize[1] * ch.mins[1];
else if (f & RS_Y_MIN_PARENT_MAX)
ch.item_position[1] = parentsize[1] + ch.mins[1];
else if (f & RS_Y_MIN_PARENT_MID)
{
ch.item_position[1] = parentsize[1]/2 + ch.mins[1];
if (ch.item_parent.frame_stdheight)
if (parentsize[1] < ch.item_parent.frame_stdheight)
{
if (ch.item_position[1] < parentsize[1]*0.5)
ch.item_position[1] += (ch.item_parent.frame_stdheight-parentsize[1])*0.5;
else
ch.item_position[1] -= (ch.item_parent.frame_stdheight-parentsize[1])*0.5;
}
}
else //if (f & RS_Y_MIN_PARENT_MIN)
ch.item_position[1] = ch.mins[1];
if (f & RS_Y_FRACTION)
ch.item_position[1] = parentsize[1] * ch.maxs[1];
else if (f & RS_Y_MAX_PARENT_MIN)
ch.item_size[1] = ch.maxs[1] - ch.item_position[1];
else if (f & RS_Y_MAX_PARENT_MID)
{
ch.item_size[1] = ch.maxs[1]+parentsize[1]/2 - ch.item_position[1];
if (ch.item_parent.frame_stdheight)
if (parentsize[1] < ch.item_parent.frame_stdheight)
{
if (ch.item_position[1]+ch.item_size[1] < parentsize[1]*0.5)
ch.item_size[1] += (ch.item_parent.frame_stdheight-parentsize[1])*0.5;
else
ch.item_size[1] -= (ch.item_parent.frame_stdheight-parentsize[1])*0.5;
}
}
else if (f & RS_Y_MAX_PARENT_MAX)
ch.item_size[1] = ch.maxs[1]+parentsize[1] - ch.item_position[1];
else //if (f & RS_Y_MAX_OWN_MIN)
ch.item_size[1] = ch.maxs[1];
};
void(mitem fromitem, string cmd) mitem_frame::item_execcommand =
{
if (item_parent)
@ -90,7 +156,8 @@ void(mitem newitem, float originflags, vector originmin, vector originmax) mitem
newitem.resizeflags = originflags;
newitem.mins = originmin;
newitem.maxs = originmax;
mitem_parentresized(newitem, [item_size[0] - item_framesize[0]*2, item_size[1] - (item_framesize[1] + item_framesize[2])]);
local vector parentsize = [item_size[0] - item_framesize[0]*2, item_size[1] - (item_framesize[1] + item_framesize[2])];
mitem_parentresized(newitem, parentsize);
newitem.item_resized();
item_flags |= IF_CLIENTMOVED; //make sure it happens.
@ -312,7 +379,7 @@ void(vector pos) mitem_vslider::item_draw =
float h = item_size[1];
float isize = w;
vector v = drawgetimagesize("scrollbars/slider.tga");
vector v = dp_workarounds?'0 0 0':drawgetimagesize("scrollbars/slider.tga");
if (v != '0 0 0')
{
@ -352,7 +419,7 @@ void(vector pos) mitem_vslider::item_draw =
};
void() mitem_vslider::mitem_vslider =
{
item_size[0] = 16;
item_size[0] = 8;
item_size[1] = 128;
};

View file

@ -0,0 +1,210 @@
#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

View file

@ -190,50 +190,7 @@ float(vector minp, vector sz) mouseinbox =
};
void(mitem ch, vector parentsize) mitem_parentresized =
{
float f = ch.resizeflags;
if (f & RS_X_FRACTION)
ch.item_position[0] = parentsize[0] * ch.mins[0];
else if (f & RS_X_MIN_PARENT_MAX)
ch.item_position[0] = parentsize[0] + ch.mins[0];
else if (f & RS_X_MIN_PARENT_MID)
ch.item_position[0] = parentsize[0]/2 + ch.mins[0];
else //if (f & RS_X_MIN_PARENT_MIN)
ch.item_position[0] = ch.mins[0];
if (f & RS_X_FRACTION)
ch.item_position[0] = parentsize[0] * ch.maxs[0];
else if (f & RS_X_MAX_PARENT_MIN)
ch.item_size[0] = ch.maxs[0] - ch.item_position[0];
else if (f & RS_X_MAX_PARENT_MID)
ch.item_size[0] = ch.maxs[0]+parentsize[0]/2 - ch.item_position[0];
else if (f & RS_X_MAX_PARENT_MAX)
ch.item_size[0] = ch.maxs[0]+parentsize[0] - ch.item_position[0];
else //if (f & RS_X_MAX_OWN_MIN)
ch.item_size[0] = ch.maxs[0];
if (f & RS_Y_FRACTION)
ch.item_position[1] = parentsize[1] * ch.mins[1];
else if (f & RS_Y_MIN_PARENT_MAX)
ch.item_position[1] = parentsize[1] + ch.mins[1];
else if (f & RS_Y_MIN_PARENT_MID)
ch.item_position[1] = parentsize[1]/2 + ch.mins[1];
else //if (f & RS_Y_MIN_PARENT_MIN)
ch.item_position[1] = ch.mins[1];
if (f & RS_Y_FRACTION)
ch.item_position[1] = parentsize[1] * ch.maxs[1];
else if (f & RS_Y_MAX_PARENT_MIN)
ch.item_size[1] = ch.maxs[1] - ch.item_position[1];
else if (f & RS_Y_MAX_PARENT_MID)
ch.item_size[1] = ch.maxs[1]+parentsize[1]/2 - ch.item_position[1];
else if (f & RS_Y_MAX_PARENT_MAX)
ch.item_size[1] = ch.maxs[1]+parentsize[1] - ch.item_position[1];
else //if (f & RS_Y_MAX_OWN_MIN)
ch.item_size[1] = ch.maxs[1];
};
void(mitem ch, vector parentsize) mitem_parentresized;
#include "mitem_frame.qc"
void() mitem::item_resized =

View file

@ -133,6 +133,118 @@ mitem(string text, string command, float height) menuitemtext_spawn =
};
class mitem_label : mitem
{ //class that queries its frame to find the text to show.
mitem_vslider vslider;
virtual void(vector pos) item_draw =
{
vector rgb = menuitem_textcolour(this);
string text = get(item_text);
float fl = 2; //top-align
vector textpos = pos;
vector clientsize = item_size;
if (item_flags & IF_CENTERALIGN)
fl |= 0; //default is to center align
else if (item_flags & IF_RIGHTALIGN)
fl |= 4; //right align.
else
fl |= 1; //left align.
if (vslider)
{
textpos[1] -= vslider.val;
clientsize[1] += vslider.val;
clientsize[0] -= vslider.item_size[0];
}
local vector omin = ui.drawrectmin, omax = ui.drawrectmax;
//clip the draw rect to our area, so text doesn't appear 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]);
clientsize[1] = drawtextfield(textpos, clientsize, fl, text) * item_scale;
ui.setcliparea(omin_x, omin_y, omax_x - omin_x, omax_y - omin_y);
if (vslider)
{
vslider.item_size[1] = item_size[1];
if (ui.mousepos != ui.oldmousepos)
{
if (mouseinbox(pos + [clientsize[0], 0], vslider.item_size))
this.item_focuschange(vslider, IF_MFOCUSED);
}
vslider.item_draw(pos + [clientsize[0], 0]);
}
}
ui.drawrectmin = omin;
ui.drawrectmax = omax;
if (clientsize[1] > item_size[1])
{ //enough text that we need a scrollbar.
if (!vslider)
vslider = spawn(mitem_vslider, val:0, stride:8);
vslider.maxv = clientsize[1] - item_size[1];
item_flags |= IF_SELECTABLE;
}
else if (vslider)
{ //the text got shorter...
vslider.item_remove();
vslider = (mitem_vslider)__NULL__;
if (item_command == "")
item_flags &~= IF_SELECTABLE;
}
};
virtual float(vector pos, float scan, float char, float down) item_keypress =
{
if (vslider)
if (vslider.item_keypress(pos, scan, char, down))
return TRUE;
if (this.item_command)
{
if (!down)
return FALSE;
if (scan == K_ENTER || (scan == K_MOUSE1 && mouseinbox(pos, this.item_size)))
{
item_parent.item_execcommand(this, this.item_command);
// localcmd(strcat(this.item_command, "\n"));
return TRUE;
}
}
return FALSE;
};
//zone+unzone input strings as needed
virtual void() item_remove =
{
if (item_command)
strunzone(item_command);
if (vslider)
{
vslider.item_remove();
vslider = (mitem_vslider)__NULL__;
}
super::item_remove();
};
void() mitem_label =
{
if (item_command != "")
{
item_command = strzone(item_command);
item_flags |= IF_SELECTABLE;
}
};
};
/***************************************************************************
basic text item.
identical to text, but includes a 3dish border.