#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