001package polaris.sdk.ui.controls;
002import polaris.sdk.ui.*;
003
004import java.util.*;
005public class Table extends Widget {
006 public interface CellEditListener{void edited(int row,int column,String value);}
007 protected String[] columns={"名称","值"};protected final java.util.List<TableCell[]> data=new ArrayList<TableCell[]>();protected int selected=-1,sortColumn=-1;protected boolean ascending=true,editable=true;protected float scroll;private float shownScroll;private CellEditListener listener;
008 public Table(){w=450;h=210;focusable=true;}
009 public Table columns(String... names){if(names==null||names.length==0)throw new IllegalArgumentException("columns");if(!data.isEmpty())throw new IllegalStateException("Set columns before adding rows");columns=names.clone();return this;}
010 public Table row(String... values){if(values.length!=columns.length)throw new IllegalArgumentException("Column count differs");TableCell[] row=new TableCell[values.length];for(int i=0;i<values.length;i++)row[i]=new TableCell(values[i]);data.add(row);return this;}
011 public int rowCount(){return data.size();}public TableCell cell(int row,int col){return data.get(row)[col];}public int selectedRow(){return selected;}
012 public Table editable(boolean b){editable=b;return this;}public Table onEdit(CellEditListener c){listener=c;return this;}
013 public void edit(int row,int col,String value){data.get(row)[col].value(value);changed();if(listener!=null)listener.edited(row,col,value);}
014 public void sort(int col){if(col<0||col>=columns.length)throw new IllegalArgumentException("column");ascending=sortColumn==col?!ascending:true;sortColumn=col;final int index=col;final boolean asc=ascending;Collections.sort(data,(a,b)->{String x=a[index].value(),y=b[index].value();int cmp;try{cmp=Double.compare(Double.parseDouble(x),Double.parseDouble(y));}catch(NumberFormatException e){cmp=x.compareToIgnoreCase(y);}return asc?cmp:-cmp;});selected=-1;changed();}
015 protected void editDialog(int row,int col){if(!editable||ui==null)return;Modal dialog=new Modal("编辑单元格 · "+columns[col]);dialog.bounds(0,0,410,185);TextField editor=new TextField(cell(row,col).value());editor.bounds(22,69,366,38);dialog.add(editor);Button apply=new Button("应用").primary(true);apply.bounds(292,126,96,35);Runnable commit=()->{edit(row,col,editor.value());dialog.close();};apply.onClick(w->commit.run());editor.onSubmit(s->commit.run());dialog.add(apply);ui.show(dialog);editor.selectAll();}
016 @Override protected void paint(Painter p){surface(p);pushContentClip(p);try{float cw=w/columns.length;shownScroll=motion("scroll",scroll,28,.98f);glassPart(p,ax+3,ay+3,w-6,31,8,.065f,theme().surface,0,theme().blur);for(int c=0;c<columns.length;c++){String label=columns[c]+(sortColumn==c?(ascending?" ↑":" ↓"):"");p.textWeight(label,ax+c*cw+12,ay+10,12,theme().text,500);}Rect body=new Rect(ax+2,ay+35,w-4,h-37).intersect(clip);p.clip(body);if(selected>=0){float sy=motion("selectedY",selected*30,28,.81f);glassPart(p,ax+4,ay+35+sy-shownScroll,w-8,29,10,theme().selectedTint,theme().accent,.28f,theme().blur);}int first=Math.max(0,(int)(shownScroll/30));for(int r=first;r<data.size();r++){float yy=ay+35+r*30-shownScroll;if(yy>ay+h)break;for(int c=0;c<columns.length;c++){p.clip(new Rect(ax+c*cw+8,Math.max(yy,ay+35),cw-16,Math.min(28,Math.max(0,yy+28-Math.max(yy,ay+35)))).intersect(clip));p.text(data.get(r)[c].value(),ax+c*cw+12,yy+8,12,ink());}p.clip(body);}p.clip(clip);}finally{popContentClip(p);p.clip(clip);}}
017 @Override public void event(UiEvent e){if(e.type==UiEvent.Type.DOWN&&e.button==1){int c=Math.max(0,Math.min(columns.length-1,(int)((e.x-ax)/(w/columns.length))));if(e.y<ay+34)sort(c);else{int r=(int)((e.y-ay-35+shownScroll)/30);if(r>=0&&r<data.size()){selected=r;changed();if(e.clicks>=2)editDialog(r,c);}}e.handled=true;}else if(e.type==UiEvent.Type.WHEEL){scroll=Math.max(0,Math.min(Math.max(0,data.size()*30-h+38),scroll+e.wheel*60));e.handled=true;}else if(e.type==UiEvent.Type.KEY){if(e.key==38||e.key==40){selected=Math.max(0,Math.min(data.size()-1,selected+(e.key==38?-1:1)));if(selected*30<scroll)scroll=selected*30;if(selected*30+30>scroll+h-38)scroll=selected*30+30-h+38;changed();e.handled=true;}else if(e.key==10&&selected>=0){editDialog(selected,0);e.handled=true;}}}
018}