EasyUniversal / DocsДокументация · SDK 0.3Подписка Universal ↗
Easy Universal / Примеры и шаблоны

templates/gui-panel/menu.as

AngelScript · native runtime · локальная сборка
// Copy into your package and list in manifest modules. Not native ui extensions.
namespace easy_ui {
    // Comparisons also reject NaN/Infinity. Geometry is bounded before drawing.
    float bounded(float value,float low,float high,float fallback) {
        if(!(value>=-100000 && value<=100000))return fallback;
        return value<low?low:(value>high?high:value);
    }
    // Variable-size flow: automatic wrapping and measured content height.
    class Flow {
        float x=0,y=0,width=240,gap=8,cursor=0,row=0,offset=0,placedWidth=0,placedHeight=0;
        void begin(float left,float top,float span) {
            x=bounded(left,-90000,90000,0);y=bounded(top,-90000,90000,0);
            width=bounded(span,24,2048,240);gap=bounded(gap,0,32,8);
            cursor=0;row=0;offset=0;
        }
        vec2 place(float span,float height) {
            span=bounded(span,24,width,width);height=bounded(height,24,2048,32);
            placedWidth=span;placedHeight=height;
            if(cursor>0&&cursor+span>width){offset+=row+gap;cursor=0;row=0;}
            vec2 position(x+cursor,y+offset);
            cursor+=span+gap;if(height>row)row=height;
            return position;
        }
        void newline(){if(row>0){offset+=row+gap;cursor=0;row=0;}}
        float content_height() const{return offset+row;}
        bool button(const string &in id,const string &in label,float span,float height=32){
            vec2 p=place(span,height);return ui::button(id,label,p.x,p.y,placedWidth,placedHeight);
        }
        bool checkbox(const string &in id,const string &in label,bool value,float span,float height=32){
            vec2 p=place(span,height);return ui::checkbox(id,label,value,p.x,p.y,placedWidth,placedHeight);
        }
        bool section(const string &in id,const string &in title,bool open){
            newline();bool hit=button(id,(open?"- ":"+ ")+title,width);newline();
            return hit?!open:open;
        }
    }
    // Deterministic layout; no implicit clipping or native input hooks.
    class Grid {
        float x=0,y=0,width=240,rowHeight=32,gap=8;
        uint columns=1,item=0;
        void begin(float left,float top,float span,uint count,float height) {
            x=bounded(left,-90000,90000,0);y=bounded(top,-90000,90000,0);
            columns=count==0?1:(count>8?8:count);gap=bounded(gap,0,32,8);
            float minimum=24*float(columns)+gap*float(columns-1);
            width=bounded(span,minimum,2048,minimum);
            rowHeight=bounded(height,24,128,32);item=0;
        }
        float cell_width() const { return (width-gap*float(columns-1))/float(columns); }
        vec2 next() {uint index=item++;return vec2(x+float(index%columns)*(cell_width()+gap),y+float(index/columns)*(rowHeight+gap));}
    }
    // Virtualized list: only rows intersecting the viewport emit commands.
    // No automatic persistence, global input or implicit native layout.
    class ScrollList {
        float offset=0,rowHeight=32,gap=8;
        uint index=0;
        bool draw(const string &in id,const array<string> &in choices,float x,float y,float width,float height) {
            uint count=choices.length();
            if(count==0 || count>256){offset=0;index=0;return false;}
            if(index>=count)index=0;
            x=bounded(x,-90000,90000,0);y=bounded(y,-90000,90000,0);
            width=bounded(width,24,2048,240);height=bounded(height,24,2048,220);
            rowHeight=bounded(rowHeight,24,128,32);gap=bounded(gap,0,32,8);
            float stride=rowHeight+gap,content=float(count)*stride-gap;
            offset=bounded(offset,0,content,0);
            offset=ui::scroll(id+"-scroll",offset,content,x,y,width,height);
            uint first=uint(offset/stride);
            bool changed=false;
            render::push_clip(x,y,width,height);
            for(uint i=first;i<count;i++) {
                float top=y+float(i)*stride-offset;
                if(top>=y+height)break;
                if(top+rowHeight<=y)continue;
                if(ui::button(id+"-item-"+i,(i==index?"> ":"  ")+choices[i],x,top,width,rowHeight)) {
                    changed=i!=index;index=i;
                }
            }
            render::pop_clip();
            return changed;
        }
    }
    // Bounded visible rows with explicit navigation. This is pagination, not
    // a native wheel-driven/clipped scroll container.
    class PagedList {
        uint page=0,index=0;
        uint pages(uint count,uint rows) const {return rows==0||count==0?0:1+(count-1)/rows;}
        void draw(const string &in id,const array<string> &in choices,uint rows,float x,float y,float width) {
            rows=rows<1?1:(rows>8?8:rows);
            uint total=pages(choices.length(),rows);
            if(total==0){page=0;index=0;return;}
            if(page>=total)page=total-1;
            if(index>=choices.length())index=0;
            // Handle navigation before drawing rows: no stale-page activations.
            if(ui::button(id+"-prev","<",x,y,width/2-4,32)&&page>0)page--;
            if(ui::button(id+"-next",">",x+width/2+4,y,width/2-4,32)&&page+1<total)page++;
            uint begin=page*rows;
            for(uint i=0;i<rows&&begin+i<choices.length();i++){
                uint choice=begin+i;
                if(ui::button(id+"-item-"+choice,(choice==index?"> ":"  ")+choices[choice],x,y+36*float(i+1),width,32))index=choice;
            }
        }
    }
    class Column {
        float x=0, y=0, width=240, gap=8;
        void begin(float left,float top,float span) { x=left; y=top; width=span; }
        vec2 next(float height) { vec2 p(x,y); y+=height+gap; return p; }
    }
    class Dropdown {
        bool open=false;
        uint index=0;
        // Call header with ordinary controls, then popup LAST to overlay them.
        void header(const string &in id,const array<string> &in choices,float x,float y,float width) {
            if(choices.length()==0 || choices.length()>8) {open=false;return;}
            if(index>=choices.length()) index=0;
            if(ui::button(id,choices[index]+(open?"  ^":"  v"),x,y,width,32)) open=!open;
        }
        void popup(const string &in id,const array<string> &in choices,float x,float y,float width) {
            if(!open || choices.length()==0 || choices.length()>8)return;
            for(uint i=0;i<choices.length();i++) {
                if(ui::button(id+"-"+i,choices[i],x,y+34*(i+1),width,32)) { index=i;open=false; }
            }
        }
    }
    class ColorPicker {
        uint color=0xff994dff;
        void draw(const string &in id,float x,float y,float width) {
            float r=float((color>>24)&255),g=float((color>>16)&255),b=float((color>>8)&255),a=float(color&255);
            r=ui::slider(id+"-r","Red",r,0,255,1,x,y,width,48);
            g=ui::slider(id+"-g","Green",g,0,255,1,x,y+52,width,48);
            b=ui::slider(id+"-b","Blue",b,0,255,1,x,y+104,width,48);
            a=ui::slider(id+"-a","Alpha",a,0,255,1,x,y+156,width,48);
            color=(uint(r)<<24)|(uint(g)<<16)|(uint(b)<<8)|uint(a);
            render::rect(x,y+210,width,20,color,true);
        }
    }
    bool section(const string &in id,const string &in title,bool expanded,float x,float y,float width) {
        if(ui::button(id,(expanded?"- ":"+ ")+title,x,y,width,32)) return !expanded;
        return expanded;
    }
}
Easy Universal — отдельный проект. Easy External не изменён.