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

net.alantea.swing.layout.percent.PercentLayout Maven / Gradle / Ivy

The newest version!
package net.alantea.swing.layout.percent;

import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Insets;
import java.awt.LayoutManager2;
import java.awt.Rectangle;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

public class PercentLayout implements LayoutManager2
{
   /** Map of constraints for components. */
   private Map map;

   /** The layout done. */
   private Map horizontalLayoutDone;
   private Map verticalLayoutDone;
   private List currently;

   /**
    * Constructor.
    */
   public PercentLayout()
   {
      this(0, 0);
   }

   /**
    * Constructor with info on full size.
    * 
    * @param fullWidth full reference width
    * @param fullHeight full reference height
    */
   public PercentLayout(int fullWidth, int fullHeight)
   {
      map = new HashMap();
      horizontalLayoutDone = new HashMap<>();
      verticalLayoutDone = new HashMap<>();
      currently = new LinkedList<>();
   }

   /*
    * (non-Javadoc)
    * 
    * @see java.awt.LayoutManager#addLayoutComponent(java.lang.String, java.awt.Component)
    */
   @Override
   public void addLayoutComponent(String name, Component comp)
   {
      // Not allowed
   }

   @Override
   public void addLayoutComponent(Component component, Object constraints)
   {
      if (constraints instanceof PercentConstraints)
      {
         map.put(component, (PercentConstraints) constraints);
      }
   }

   /*
    * (non-Javadoc)
    * 
    * @see java.awt.LayoutManager#removeLayoutComponent(java.awt.Component)
    */
   @Override
   public void removeLayoutComponent(Component comp)
   {
      map.remove(comp);
   }

   @Override
   public Dimension minimumLayoutSize(Container parent)
   {
//      Container relativeSizeParent = parent;
//      // keep size as it is.
//      return relativeSizeParent.getSize();
      return new Dimension(1, 1);
   }

   @Override
   public Dimension preferredLayoutSize(Container parent)
   {
      int width = 1;
      int height = 1;
      for (Component component : parent.getComponents())
      {
         PercentConstraints constraints = map.get(component);
         if (constraints != null)
         {
            double mpw = constraints.getWPercent();
            double mph = constraints.getHPercent();
            Dimension compDim = component.getPreferredSize();
            
            int w = getSizeValue(mpw, width, compDim.width);
            int h = getSizeValue(mph, height, compDim.height);
            width = Math.max(width,  w);
            height = Math.max(height, h);
         }
      }
      return new Dimension(width, height);
   }
   
   private int getSizeValue(double constr, int currentSize, int compDim)
   {
      int size = 0;
      if (constr <= 1)
      {
         size =  (int) ((constr > 0) ? compDim / constr : 1);
      }
      else
      {
         size = (int) (currentSize + constr);
      }
      return size;
   }

   @Override
   public float getLayoutAlignmentX(Container parent)
   {
      return 0;
   }

   @Override
   public float getLayoutAlignmentY(Container parent)
   {
      return 0;
   }

   @Override
   public void invalidateLayout(Container parent)
   {
      // do nothing
   }

   @Override
   public Dimension maximumLayoutSize(Container parent)
   {
      return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
   }

   @Override
   public void layoutContainer(Container parent)
   {
      horizontalLayoutDone.clear();
      verticalLayoutDone.clear();
      currently.clear();
      
      Container relativeSizeParent = parent;
      Insets insets = relativeSizeParent.getInsets();
      int w = relativeSizeParent.getWidth() - (insets.left + insets.right);
      int h = relativeSizeParent.getHeight() - (insets.top + insets.bottom);

      // Do not manage if no constraints
      for (Component component : parent.getComponents())
      {
         PercentConstraints constraints = map.get(component);
         if (constraints == null)
         {
            Rectangle rect = component.getBounds();
            horizontalLayoutDone.put(component,  new PosAndSize(rect.x, rect.width));
            verticalLayoutDone.put(component,  new PosAndSize(rect.y, rect.height));
         }
      }
      for (Component component : parent.getComponents())
      {
         doLayout(component, 0, 0, w, h);
      }
   }

   /**
    * Do layout.
    *
    * @param component the comp
    * @param panelX the panel X
    * @param panelY the panel Y
    * @param panelW the w
    * @param panelH the h
    */
   private void doLayout(Component component, int panelX, int panelY, int panelW, int panelH)
   {
      PercentConstraints constraints = map.get(component);
      if (!horizontalLayoutDone.keySet().contains(component))
      {
         doDirectionalLayout(component,
               panelX,
               getReferenceSize(constraints.getWReferent(), panelW, panelH, Direction.HORIZONTAL),
               constraints.getXPercent(), constraints.getWPercent(),
               constraints.getXReferent(), constraints.getWReferent(),
               horizontalLayoutDone,
               true
               );
      }
      PosAndSize posAndSizeX = horizontalLayoutDone.get(component);
      if (!verticalLayoutDone.keySet().contains(component))
      {
         doDirectionalLayout(component,
               panelY,
               getReferenceSize(constraints.getHReferent(), panelY, panelH, Direction.VERTICAL),
               constraints.getYPercent(), constraints.getHPercent(),
               constraints.getYReferent(), constraints.getHReferent(),
               verticalLayoutDone,
               false
               );
      }
      PosAndSize posAndSizeY = verticalLayoutDone.get(component);
      component.setBounds(posAndSizeX.position, posAndSizeY.position, posAndSizeX.size, posAndSizeY.size);
   }
   
   private int getReferenceSize(Referent referent, int panelW, int panelH, Direction defaultValue)
   {
      Direction how = (referent != null) ? referent.getHow() : defaultValue;
      if (how == Direction.HORIZONTAL)
      {
         return panelW;
      }
      else
      {
         return panelH;
      }
   }

   /**
    * Do directional layout.
    *
    * @param component the component
    * @param panelPos the panel usable position
    * @param panelSize the panel usable size
    */
   private void doDirectionalLayout(Component component, int panelPos, int panelSize,
         double posConstraint, double sizeConstraint,
         Referent posReferent, Referent sizeReferent,
         Map doneMap,
         boolean isHorizontal)
   {
      if (doneMap.keySet().contains(component))
      {
         return;
      }
      currently.add(component);
      if ((posReferent != null) && ((posReferent.getWho() != null) && (!doneMap.keySet().contains(posReferent.getWho()))))
      {
         if (currently.contains(posReferent.getWho()))
         {
            // Abort : cycle detected
            System.out.println("Abort : cycle detected");
            return;
         }
         PercentConstraints refconstraints = map.get(posReferent.getWho());
         if (refconstraints != null)
         {
            doDirectionalLayout(posReferent.getWho(), panelPos, panelSize,
                  refconstraints.getXPercent(), refconstraints.getWPercent(),
                  refconstraints.getXReferent(), refconstraints.getWReferent(),
                  doneMap,
                  isHorizontal
                  );
         }
      }
      if ((sizeReferent != null) && ((sizeReferent.getWho() != null) && (!doneMap.keySet().contains(sizeReferent.getWho()))))
      {
         if (currently.contains(sizeReferent.getWho()))
         {
            // Abort : cycle detected
            System.out.println("Abort : cycle detected");
            return;
         }
         PercentConstraints refconstraints = map.get(sizeReferent.getWho());
         if (refconstraints != null)
         {
            doDirectionalLayout(sizeReferent.getWho(), panelPos, panelSize,
                  refconstraints.getXPercent(), refconstraints.getWPercent(),
                  refconstraints.getXReferent(), refconstraints.getWReferent(),
                  doneMap,
                  isHorizontal
                  );
         }
      }

      // Default values
      int position = panelPos + (int) (panelSize * posConstraint);
      int size =  (int) (panelSize * sizeConstraint);

      // Manage special cases
      if ((sizeReferent != null) && (sizeReferent.getWho() != null))
      {
         PosAndSize posAndSize = doneMap.get(sizeReferent.getWho());
         if (posAndSize == null)
         {
            posAndSize = new PosAndSize(0, 100);
         }
         switch (sizeReferent.getWhere())
         {
            // same size
            case SAME :
               size += posAndSize.size;
               break;

            // fill space after referent
            case AFTER:
               size = panelSize - posAndSize.position - posAndSize.size - size;
               break;

            // fill space before referent
            case BEFORE:
               size = posAndSize.position - position - size;
               break;
               
            case FAR:
               // To be set after position is calculated
               break;
               
            case START:
               // only for position
               break;
               
            case CENTER:
               // only for position
               break;
               
            case END:
               // only for position
               break;
               
            case ABSOLUTE:
               size = (int)sizeConstraint;
               break;
               
            case PREFERRED:
               size = isHorizontal ? component.getPreferredSize().width : component.getPreferredSize().height;
               break;
               
            default:
               break;
         }
      }
      else if ((sizeReferent != null) && (sizeReferent.getWhere() == Where.ABSOLUTE))
      {
         size = (int)sizeConstraint;
      }
      else if ((sizeReferent != null) && (sizeReferent.getWhere() == Where.PREFERRED))
      {
         size = isHorizontal ? component.getPreferredSize().width : component.getPreferredSize().height;
      }
      
      if ((posReferent != null) && (posReferent.getWho() != null))
      {
         PosAndSize posAndSize = doneMap.get(posReferent.getWho());
         if (posAndSize == null)
         {
            posAndSize = new PosAndSize(0, 100);
         }
         switch (posReferent.getWhere())
         {
            case AFTER :
               position += posAndSize.position + posAndSize.size;
               break;

            case SAME :
            case START :
               position += posAndSize.position;
               break;
               
            case BEFORE:
               position += posAndSize.position - size;
               break;
               
            case CENTER:
               position += posAndSize.position + (posAndSize.size - size) / 2;
               break;
               
            case END:
               position += posAndSize.position + posAndSize.size - size;
               break;
               
            case FAR:
               // only for size
               break;
               
            case ABSOLUTE:
               position = (int)posConstraint;
               break;
               
            case PREFERRED:
               break;
               
            default:
               break;
         }
//         
//         if ((sizeReferent != null) && (sizeReferent.getWhere() != null))
//         {
//         }
      }
      else if ((posReferent != null) && (posReferent.getWhere() == Where.ABSOLUTE))
      {
         position = (int)posConstraint;
      }
      
      doneMap.put(component,  new PosAndSize(position, size));
      currently.remove(component);
      return;
   }
   
   private class PosAndSize
   {
      int position;
      int size;
      
      PosAndSize(int position, int size)
      {
         this.position = position;
         this.size = size;
      }
   }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy