All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.codename1.ui.spinner.SpinnerNode Maven / Gradle / Ivy

There is a newer version: 7.0.167
Show newest version
/*
 * 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