org.smallmind.nutsnbolts.layout.SerialBox Maven / Gradle / Ivy
/*
* Copyright (c) 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020 David Berkman
*
* This file is part of the SmallMind Code Project.
*
* The SmallMind Code Project is free software, you can redistribute
* it and/or modify it under either, at your discretion...
*
* 1) The terms of GNU Affero General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* ...or...
*
* 2) The terms of the Apache License, Version 2.0.
*
* The SmallMind Code Project is distributed in the hope that it will
* be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License or Apache License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* and the Apache License along with the SmallMind Code Project. If not, see
* or .
*
* Additional permission under the GNU Affero GPL version 3 section 7
* ------------------------------------------------------------------
* If you modify this Program, or any covered work, by linking or
* combining it with other code, such other code is not for that reason
* alone subject to any of the requirements of the GNU Affero GPL
* version 3.
*/
package org.smallmind.nutsnbolts.layout;
import java.util.Iterator;
import java.util.LinkedList;
import org.smallmind.nutsnbolts.lang.UnknownSwitchCaseException;
// Lays out items one after another, in the Bias direction, with growth/shrink values also in the bias direction
public class SerialBox extends Box {
private Justification justification;
private double gap;
private boolean greedy;
protected SerialBox (ParaboxLayout layout) {
this(layout, Gap.UNRELATED);
}
protected SerialBox (ParaboxLayout layout, boolean greedy) {
this(layout, Gap.UNRELATED, greedy);
}
protected SerialBox (ParaboxLayout layout, Gap gap) {
this(layout, gap.getGap(layout.getContainer().getPlatform()));
}
protected SerialBox (ParaboxLayout layout, Gap gap, boolean greedy) {
this(layout, gap.getGap(layout.getContainer().getPlatform()), greedy);
}
protected SerialBox (ParaboxLayout layout, double gap) {
this(layout, gap, Justification.LEADING);
}
protected SerialBox (ParaboxLayout layout, double gap, boolean greedy) {
this(layout, gap, Justification.LEADING, greedy);
}
protected SerialBox (ParaboxLayout layout, Justification justification) {
this(layout, Gap.UNRELATED, justification);
}
protected SerialBox (ParaboxLayout layout, Justification justification, boolean greedy) {
this(layout, Gap.UNRELATED, justification, greedy);
}
protected SerialBox (ParaboxLayout layout, Gap gap, Justification justification) {
this(layout, gap.getGap(layout.getContainer().getPlatform()), justification);
}
protected SerialBox (ParaboxLayout layout, Gap gap, Justification justification, boolean greedy) {
this(layout, gap.getGap(layout.getContainer().getPlatform()), justification, greedy);
}
protected SerialBox (ParaboxLayout layout, double gap, Justification justification) {
this(layout, gap, justification, false);
}
protected SerialBox (ParaboxLayout layout, double gap, Justification justification, boolean greedy) {
super(SerialBox.class, layout);
this.justification = justification;
this.gap = gap;
this.greedy = greedy;
}
public double getGap () {
return gap;
}
public SerialBox setGap (Gap gap) {
return setGap(gap.getGap(getLayout().getContainer().getPlatform()));
}
public SerialBox setGap (double gap) {
this.gap = gap;
return this;
}
public Justification getJustification () {
return justification;
}
public SerialBox setJustification (Justification justification) {
this.justification = justification;
return this;
}
public boolean isGreedy () {
return greedy;
}
public SerialBox setGreedy (boolean greedy) {
this.greedy = greedy;
return this;
}
@Override
public double calculateMinimumMeasurement (Bias bias, LayoutTailor tailor) {
return calculateMeasurement(bias, TapeMeasure.MINIMUM, tailor);
}
@Override
public double calculatePreferredMeasurement (Bias bias, LayoutTailor tailor) {
return calculateMeasurement(bias, TapeMeasure.PREFERRED, tailor);
}
@Override
public double calculateMaximumMeasurement (Bias bias, LayoutTailor tailor) {
return greedy ? Integer.MAX_VALUE : calculateMeasurement(bias, TapeMeasure.MAXIMUM, tailor);
}
private synchronized double calculateMeasurement (Bias bias, TapeMeasure tapeMeasure, LayoutTailor tailor) {
double total = 0.0D;
if (!getElements().isEmpty()) {
boolean first = true;
for (ParaboxElement> element : getElements()) {
total += tapeMeasure.getMeasure(bias, element, tailor);
if (!first) {
total += gap;
}
first = false;
}
}
return total;
}
@Override
public synchronized void doLayout (Bias bias, double containerPosition, double containerMeasurement, LayoutTailor tailor) {
if (!getElements().isEmpty()) {
double preferredContainerMeasure;
if (containerMeasurement <= calculateMeasurement(bias, TapeMeasure.MINIMUM, tailor)) {
double currentMeasure;
double top = 0;
for (ParaboxElement> element : getElements()) {
tailor.applyLayout(bias, containerPosition + top, currentMeasure = element.getMinimumMeasurement(bias, tailor), element);
top += currentMeasure + gap;
}
} else if (containerMeasurement <= (preferredContainerMeasure = calculateMeasurement(bias, TapeMeasure.PREFERRED, tailor))) {
double[] preferredBiasedMeasurements = new double[getElements().size()];
double[] fat = new double[getElements().size()];
double currentMeasure;
double totalShrink = 0;
double totalFat = 0;
double top = 0;
int index;
index = 0;
for (ParaboxElement> element : getElements()) {
totalShrink += element.getConstraint().getShrink();
totalFat += (fat[index] = (preferredBiasedMeasurements[index++] = element.getPreferredMeasurement(bias, tailor)) - element.getMinimumMeasurement(bias, tailor));
}
index = 0;
for (ParaboxElement> element : getElements()) {
double totalRatio = (totalShrink + totalFat == 0) ? 0 : (element.getConstraint().getShrink() + fat[index]) / (totalShrink + totalFat);
tailor.applyLayout(bias, containerPosition + top, currentMeasure = preferredBiasedMeasurements[index++] - (totalRatio * (preferredContainerMeasure - containerMeasurement)), element);
top += currentMeasure + gap;
}
} else {
LinkedList reorderedElements = new LinkedList();
double[] tentativeMeasurements = new double[getElements().size()];
double[] maximumMeasurements = new double[getElements().size()];
double unused = containerMeasurement - preferredContainerMeasure;
double totalGrow = 0;
int index = 0;
for (ParaboxElement> element : getElements()) {
double grow;
if ((grow = element.getConstraint().getGrow()) > 0) {
totalGrow += grow;
reorderedElements.add(new ReorderedElement(element, index));
}
tentativeMeasurements[index] = element.getPreferredMeasurement(bias, tailor);
maximumMeasurements[index++] = element.getMaximumMeasurement(bias, tailor);
}
if (!reorderedElements.isEmpty()) {
do {
Iterator reorderedElementIter = reorderedElements.iterator();
double used = 0;
double spentGrowth = 0;
while (reorderedElementIter.hasNext()) {
ReorderedElement reorderedElement = reorderedElementIter.next();
double currentUnused;
double currentGrow;
if ((tentativeMeasurements[reorderedElement.getOriginalIndex()] + (currentUnused = ((currentGrow = reorderedElement.getReorderedElement().getConstraint().getGrow()) / totalGrow * unused))) < maximumMeasurements[reorderedElement.getOriginalIndex()]) {
used += currentUnused;
tentativeMeasurements[reorderedElement.getOriginalIndex()] += currentUnused;
} else {
used += maximumMeasurements[reorderedElement.getOriginalIndex()] - tentativeMeasurements[reorderedElement.getOriginalIndex()];
tentativeMeasurements[reorderedElement.getOriginalIndex()] = maximumMeasurements[reorderedElement.getOriginalIndex()];
spentGrowth += currentGrow;
reorderedElementIter.remove();
}
}
unused -= used;
totalGrow -= spentGrowth;
} while ((!reorderedElements.isEmpty()) && (unused >= 1.0));
}
switch (getJustification()) {
case FIRST:
applyLayouts(bias, containerPosition, true, tentativeMeasurements, tailor);
break;
case LAST:
applyLayouts(bias, containerPosition + containerMeasurement, false, tentativeMeasurements, tailor);
break;
case LEADING:
if (!bias.equals(getLayout().getContainer().getPlatform().getOrientation().getBias())) {
applyLayouts(bias, containerPosition, true, tentativeMeasurements, tailor);
} else {
switch (getLayout().getContainer().getPlatform().getOrientation().getFlow()) {
case FIRST_TO_LAST:
applyLayouts(bias, containerPosition, true, tentativeMeasurements, tailor);
break;
case LAST_TO_FIRST:
applyLayouts(bias, containerPosition + containerMeasurement, false, tentativeMeasurements, tailor);
break;
default:
throw new UnknownSwitchCaseException(getLayout().getContainer().getPlatform().getOrientation().getFlow().name());
}
}
break;
case TRAILING:
if (!bias.equals(getLayout().getContainer().getPlatform().getOrientation().getBias())) {
applyLayouts(bias, containerPosition + containerMeasurement, false, tentativeMeasurements, tailor);
} else {
switch (getLayout().getContainer().getPlatform().getOrientation().getFlow()) {
case FIRST_TO_LAST:
applyLayouts(bias, containerPosition + containerMeasurement, false, tentativeMeasurements, tailor);
break;
case LAST_TO_FIRST:
applyLayouts(bias, containerPosition, true, tentativeMeasurements, tailor);
break;
default:
throw new UnknownSwitchCaseException(getLayout().getContainer().getPlatform().getOrientation().getFlow().name());
}
}
break;
case CENTER:
applyLayouts(bias, containerPosition + (unused / 2), true, tentativeMeasurements, tailor);
break;
default:
throw new UnknownSwitchCaseException(getJustification().name());
}
}
}
}
private void applyLayouts (Bias bias, double top, Boolean forward, double[] tentativeMeasurements, LayoutTailor tailor) {
int index = 0;
for (ParaboxElement> element : getElements()) {
if (forward) {
tailor.applyLayout(bias, top, tentativeMeasurements[index], element);
top += tentativeMeasurements[index++] + gap;
} else {
tailor.applyLayout(bias, top -= tentativeMeasurements[index], tentativeMeasurements[index++], element);
top -= gap;
}
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy