com.codename1.ui.spinner.SpinnerNode Maven / Gradle / Ivy
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package com.codename1.ui.spinner;
import com.codename1.ui.Component;
import com.codename1.ui.Graphics;
import com.codename1.ui.Label;
import com.codename1.ui.Painter;
import com.codename1.ui.Transform;
import com.codename1.ui.events.DataChangedListener;
import com.codename1.ui.events.ScrollListener;
import com.codename1.ui.events.SelectionListener;
import com.codename1.ui.geom.Rectangle;
import com.codename1.ui.geom.Rectangle2D;
import com.codename1.ui.list.ListModel;
import com.codename1.ui.plaf.Style;
import com.codename1.ui.scene.Bounds;
import com.codename1.ui.scene.Node;
import com.codename1.ui.scene.NodePainter;
import com.codename1.ui.scene.Point3D;
import com.codename1.ui.scene.TextPainter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* A spinner node for rendering spinnable lists.
* @author shannah
*/
class SpinnerNode extends Node {
private Label rowTemplate = new Label("", "Spinner3DRow");
private Label overlayTemplate = new Label("", "Spinner3DOverlay");
private Style rowStyle, selectedRowStyle, overlayStyle;
private Map childIndex = new HashMap();
private List scrollListeners;
private SelectionListener selectionListener = new SelectionListener() {
@Override
public void selectionChanged(int oldSelected, int newSelected) {
if (newSelected < 0 && listModel != null) {
newSelected = listModel.getSelectedIndex();
}
if (newSelected >= 0 && newSelected < listModel.getSize() && newSelected != selectedIndex) {
setSelectedIndex(newSelected);
}
}
};
private DataChangedListener listChangedListener = new DataChangedListener() {
@Override
public void dataChanged(int type, int index) {
rebuildChildren();
}
};
public void addScrollListener(ScrollListener l) {
if (scrollListeners == null) {
scrollListeners = new ArrayList();
}
scrollListeners.add(l);
}
public void removeScrollListener(ScrollListener l) {
if (scrollListeners != null) {
scrollListeners.remove(l);
if (scrollListeners.isEmpty()) {
scrollListeners = null;
}
}
}
private void fireScrollEvent(int scrollPos) {
if (scrollListeners != null) {
for (ScrollListener l : scrollListeners) {
l.scrollChanged(-1, scrollPos, -1, -1);
}
}
}
public static interface RowFormatter {
public String format(String input);
}
//private double flatListHeight;
private RowFormatter rowFormatter;
private double flatScrollPos;
private int numSides = 14;
private Label renderer = new Label("Testing", "Spinner3DRow");
ListModel listModel;
Node selectedRowOverlay = new Node();
private static boolean usePerspective() {
// Disabling perspective for now because need to work out a few issues
return false; //Transform.isPerspectiveSupported();
}
public SpinnerNode() {
rowStyle = rowTemplate.getUnselectedStyle();
selectedRowStyle = rowTemplate.getSelectedStyle();
overlayStyle = overlayTemplate.getUnselectedStyle();
//Component overlayRenderer = new Label();
//overlayRenderer.setUIID("Spinner3DOverlay");
//((Label)overlayRenderer).setShowEvenIfBlank(true);
//selectedRowOverlay.setRenderer(overlayRenderer);
selectedRowOverlay.setStyle(overlayStyle);
selectedRowOverlay.setRenderer(new NodePainter() {
@Override
public void paint(Graphics g, Rectangle bounds, Node node) {
Style style = node.getStyle();
g.setColor(style.getBgColor());
g.fillRect(bounds.getX(), bounds.getY(), bounds.getWidth(), bounds.getHeight());
g.setColor(style.getFgColor());
g.drawLine(bounds.getX(), bounds.getY(), bounds.getWidth() + bounds.getX(), bounds.getY());
g.drawLine(bounds.getX(), bounds.getY()+bounds.getHeight(),
bounds.getX() + bounds.getWidth(), bounds.getY() + bounds.getHeight()
);
}
});
}
public void setRowFormatter(RowFormatter formatter) {
if (rowFormatter != formatter) {
rowFormatter = formatter;
rebuildChildren();
}
}
public Style getRowStyle() {
return rowStyle;
}
public int getNumSides() {
return numSides;
}
public Style getSelectedRowStyle() {
return selectedRowStyle;
}
public Style getSelectedOverlayStyle() {
return overlayStyle;
}
public ListModel getListModel() {
return listModel;
}
private void rebuildChildren() {
childIndex.clear();
removeAll();
/*
if (listModel != null) {
ListModel list = listModel;
int len = list.getSize();
for (int i=0; i list) {
if (listModel != null) {
listModel.removeSelectionListener(selectionListener);
listModel.removeDataChangedListener(listChangedListener);
}
listModel = list;
if (listModel != null) {
listModel.addSelectionListener(selectionListener);
listModel.addDataChangedListener(listChangedListener);
}
rebuildChildren();
}
//public void refreshStyles() {
// if (hasChildren()) {
// for (Node n : getChildNodes()) {
// if (n == selectedRowOverlay) {
//
// } else {
// //n.getRenderer().setUnselectedStyle(getRowStyle());
// //n.getRenderer().setSelectedStyle(getSelectedRowStyle());
// }
//
// }
// }
//}
public Node getSelectedRowOverlay() {
return selectedRowOverlay;
}
public double calcRowHeight() {
return renderer.getPreferredH();
}
public double calcFlatListHeight() {
return renderer.getPreferredH() * listModel.getSize();
}
public double calcViewportHeight() {
double circumference = renderer.getPreferredH() * numSides;
double diameter = circumference / Math.PI;
return diameter;
}
private double calculateRotationForChild(int index) {
double degreeOffset = flatScrollPos * 360.0 / (numSides * renderer.getPreferredH());
int pos = index % numSides;
return (-(360.0 / numSides) * pos + degreeOffset) % 360;
}
private double getRotationRangeForSide() {
return 360.0 / numSides;
}
private double getFlatVisibleHeight() {
return renderer.getPreferredH() * numSides / 2;
}
public int getSelectedIndex() {
return (int)(flatScrollPos / calcFlatListHeight() * listModel.getSize());
}
public void setSelectedIndex(int index) {
if (index < 0 || index > listModel.getSize()-1) {
throw new ArrayIndexOutOfBoundsException("Index out of bounds:"+index+", must be between 0 and "+listModel.getSize());
}
setScrollY(index * calcFlatListHeight() / listModel.getSize());
}
private int selectedIndex=-1;
private List selectionListeners;
private void updateSelectedIndex() {
int newSelectedIndex = getSelectedIndex();
if (newSelectedIndex != selectedIndex) {
int oldSelectedIndex = selectedIndex;
selectedIndex = newSelectedIndex;
listModel.setSelectedIndex(newSelectedIndex);
if (selectionListeners != null && !selectionListeners.isEmpty()) {
for (SelectionListener l : selectionListeners) {
l.selectionChanged(oldSelectedIndex, newSelectedIndex);
}
}
}
}
public void addSelectionListener(SelectionListener l) {
if (selectionListeners == null) {
selectionListeners = new ArrayList();
}
selectionListeners.add(l);
}
public void removeSelectionListener(SelectionListener l) {
if (selectionListeners != null) {
selectionListeners.remove(l);
}
if (selectionListeners.isEmpty()) {
selectionListeners = null;
}
}
private int getMinVisibleIndex(int selectedIndex) {
return selectedIndex - numSides/4;
}
private int getMaxVisibleIndex(int selectedIndex) {
return selectedIndex + numSides/4;
}
public void setScrollY(double pos) {
boolean changed = Math.abs(pos-flatScrollPos) > 2;
this.flatScrollPos = pos;
setNeedsLayout(true);
if (getScene() != null) {
getScene().repaint();
}
updateSelectedIndex();
if (changed) {
fireScrollEvent((int)pos);
}
}
public double getScrollY() {
return flatScrollPos;
}
private Node getOrCreateChild(int i) {
if (childIndex.containsKey(i)) {
return childIndex.get(i);
}
if (listModel != null) {
ListModel list = listModel;
//int len = list.getSize();
//for (int i=0; i index || maxVisibleIndex < index) && !childIndex.containsKey(i)) {
index++;
continue;
}
Node child = getOrCreateChild(i);
//for (Node child : getChildNodes()) {
if (minVisibleIndex > index || maxVisibleIndex < index) {
child.visible.set(false);
index++;
continue;
}
//renderer.setText(listModel.getItemAt(index));
//child.setRenderer(renderer);
child.visible.set(true);
Bounds localBounds = child.boundsInLocal.get();
localBounds.setWidth(width);
localBounds.setDepth(0);
localBounds.setHeight(diameter);
localBounds.setMinX(0.0);
localBounds.setMinY(0.0);
child.paintingRect.set(new Rectangle(0, (int)(diameter/2 - rendererHeight/2), (int)width, (int)rendererHeight));
//child.localCanvasZ.set(diameter/2); // So that rotation works correctly
if (usePerspective()) {
localBounds.setDepth(diameter);
double angle = calculateRotationForChild(index);
if (Math.abs(angle) < 10) {
//Log.p("Settin focus");
child.addTags("selected");
child.setStyle(getSelectedRowStyle());
child.opacity.set(1.0);
} else {
child.removeTags("selected");
double opacity = Math.cos(angle * Math.PI / 180);
child.setStyle(getRowStyle());
child.opacity.set(opacity);
//Log.p("Opacity="+opacity);
//child.getRenderer().getStyle().setOpacity(Math.min(255, Math.max(0, opacity)));
}
child.rotate.set(-angle);
child.rotationAxis.set(new Point3D(1, 0, 0)); // Rotate along X-Axis
child.layoutX.set(0.0);
child.layoutY.set(0.0);
child.layoutZ.set(-diameter/2);
} else {
//System.out.println("Item "+listModel.getItemAt(index));
double angle = calculateRotationForChild(index) * Math.PI / 180.0;
//Log.p("Angle["+index+"]="+angle);
//System.out.println("Angle "+angle);
double minAngle = angle + getRotationRangeForSide() * Math.PI/180.0 / 2;
//System.out.println("Min angle: "+minAngle);
double maxAngle = angle - getRotationRangeForSide() * Math.PI/180.0 /2;
//Log.p("Angle: "+Math.abs(angle)+" rot range: "+(getRotationRangeForSide() * Math.PI/180.0));
if (Math.abs(angle) < 10 * Math.PI/180) {
//Log.p("Settin focus");
child.addTags("selected");
child.setStyle(selectedRowStyle);
child.opacity.set(1.0);
} else {
child.removeTags("selected");
child.setStyle(rowStyle);
double opacity = Math.cos(angle);
child.opacity.set(opacity);
}
//System.out.println("Max angle:"+maxAngle);
double projectedHeight = Math.abs((diameter/2) * (Math.sin(minAngle) - Math.sin(maxAngle)));
//System.out.println("Projected height "+projectedHeight);
child.layoutX.set(0.0);
child.layoutY.set(-(diameter/2) * Math.sin(angle));
child.layoutZ.set(0.0);
child.scaleY.set(projectedHeight / rendererHeight);
}
index++;
}
//if (child == selectedRowOverlay) {
Bounds b = selectedRowOverlay.boundsInLocal.get();
b.setWidth(width);
b.setHeight(rendererHeight);
b.setMinX(0);
b.setMinY(0);
selectedRowOverlay.layoutX.set(0.0);
selectedRowOverlay.layoutY.set(diameter/2 - rendererHeight/2);
//index++;
//continue;
// }
}
}
//@Override
public void render(Graphics g) {
g.setColor(overlayStyle.getBgColor());
int alpha = g.getAlpha();
g.setAlpha(255);
g.fillRect(0, 0, (int)boundsInLocal.get().getWidth(), (int)boundsInLocal.get().getHeight());
g.setAlpha(alpha);
super.render(g);
int clipX = g.getClipX();
int clipY = g.getClipY();
int clipW = g.getClipWidth();
int clipH = g.getClipHeight();
Rectangle2D overlayRect = selectedRowOverlay.getBoundsInScene(new Rectangle2D());
double magnification = 1.35;
double oldScaleX = scaleX.get();
double oldScaleY = scaleY.get();
double oldTranslateX = translateX.get();
scaleX.set(oldScaleX*magnification);
scaleY.set(oldScaleY*magnification);
switch (getRowStyle().getAlignment()) {
case Component.LEFT:
translateX.set(oldTranslateX + boundsInLocal.get().getWidth() * (magnification - 1.0)/2/magnification);
break;
case Component.RIGHT:
translateX.set(oldTranslateX - boundsInLocal.get().getWidth() * (magnification - 1.0)/2/magnification);
break;
}
selectedRowOverlay.visible.set(false);
g.setClip(
(int)overlayRect.getX(),
(int)overlayRect.getY()+1,
(int)overlayRect.getWidth(),
(int)overlayRect.getHeight()-2
);
super.render(g);
selectedRowOverlay.visible.set(true);
g.setClip(clipX, clipY, clipW, clipH);
scaleX.set(oldScaleX);
scaleY.set(oldScaleY);
translateX.set(oldTranslateX);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy