001package polaris.sdk.ui.controls; 002import polaris.sdk.ui.*; 003 004/** Grid cells use the same symmetric list inset and conditional scrollbar gutter. */ 005public class GridView extends ListBox { 006 private int columns=3;private float cellHeight=76,gridScroll; 007 public GridView(){w=320;h=175;} 008 public GridView columns(int n){if(n<1)throw new IllegalArgumentException("columns");columns=n;return this;} 009 @Override protected float contentHeight(){return items.isEmpty()?0:((items.size()+columns-1)/columns)*cellHeight-Spacing.GAP;} 010 @Override protected void ensureVisible(){if(selected>=0){float top=(selected/columns)*cellHeight,bottom=top+cellHeight-Spacing.GAP;if(top<scroll)scroll=top;if(bottom>scroll+viewportHeight())scroll=bottom-viewportHeight();}scroll=Math.max(0,Math.min(maxScroll(),scroll));} 011 @Override protected void paint(Painter p){ 012 if(glass)surface(p);pushContentClip(p); 013 try{Rect inner=contentBounds();p.clip(inner.intersect(clip));float cell=Math.max(0,(inner.w-Spacing.GAP*(columns-1))/columns);gridScroll=visualScroll; 014 int first=Math.max(0,(int)(gridScroll/cellHeight)*columns),last=Math.min(items.size(),first+((int)(inner.h/cellHeight)+2)*columns); 015 for(int i=first;i<last;i++){float xx=inner.x+(i%columns)*(cell+Spacing.GAP),yy=inner.y+(i/columns)*cellHeight-gridScroll;float active=MotionValue.clamp(motion("cell"+i,i==selected?1:0,25,.84f)); 016 glassPart(p,xx,yy,cell,cellHeight-Spacing.GAP,theme().radius,active*theme().selectedTint,theme().accent,.36f*active,theme().blur); 017 p.clip(new Rect(xx,yy,cell,cellHeight-Spacing.GAP).intersect(inner).intersect(clip));p.text("◇",xx+12,yy+9,22,theme().text);p.text(items.get(i),xx+10,yy+40,12,ink());p.clip(inner.intersect(clip)); 018 }p.clip(clip);paintScrollbar(p); 019 }finally{popContentClip(p);p.clip(clip);} 020 } 021 @Override public void event(UiEvent e){ 022 if(e.type==UiEvent.Type.DOWN&&e.button==1&&contentBounds().contains(e.x,e.y)){ 023 Rect inner=contentBounds();float cell=Math.max(0,(inner.w-Spacing.GAP*(columns-1))/columns),xx=e.x-inner.x,yy=e.y-inner.y+gridScroll; 024 int col=(int)Math.floor(xx/(cell+Spacing.GAP)),row=(int)Math.floor(yy/cellHeight),i=row*columns+col; 025 if(yy>=0&&col>=0&&col<columns&&xx-col*(cell+Spacing.GAP)<cell&&yy-row*cellHeight<cellHeight-Spacing.GAP&&i>=0&&i<items.size())select(i);e.handled=true; 026 }else if(e.type==UiEvent.Type.KEY&&(e.key==37||e.key==39||e.key==38||e.key==40)){int d=e.key==37?-1:e.key==39?1:e.key==38?-columns:columns;if(!items.isEmpty())select(Math.max(0,Math.min(items.size()-1,selected+d)));e.handled=true;} 027 else super.event(e); 028 } 029}