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}