blob: ca631da8ac6d8396f2e499622be09b79898433c7 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2010 BSI Business Systems Integration AG.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* BSI Business Systems Integration AG - initial API and implementation
******************************************************************************/
package org.eclipse.scout.rt.ui.swt;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.scout.commons.CompositeObject;
import org.eclipse.scout.commons.logger.IScoutLogger;
import org.eclipse.scout.commons.logger.ScoutLogManager;
import org.eclipse.scout.rt.client.ui.form.fields.IFormField;
import org.eclipse.scout.rt.ui.swt.basic.SwtScoutComposite;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Layout;
import org.eclipse.ui.forms.widgets.ILayoutExtension;
/**
* Dynamic layout using logical grid data {@link LogicalGridData} to arrange
* fields. The grid data per field can be passed when adding the component to
* the container or set as client property with name {@link LogicalGridData#CLIENT_PROPERTY_NAME}.
*/
public class LogicalGridLayout extends Layout implements ILayoutExtension {
private static final IScoutLogger LOG = ScoutLogManager.getLogger(LogicalGridLayout.class);
public static final int MIN = 0;
public static final int PREF = 1;
public static final int MAX = 2;
public static final float EPS = 1E-6f;
private boolean m_debug;
private final int m_hgap;
private final int m_vgap;
private final int m_minWidth;
private LogicalGridLayoutInfo m_info;
private CompositeObject m_infoCacheKey;
public LogicalGridLayout(int hgap, int vgap) {
this(hgap, vgap, -1);
}
public LogicalGridLayout(int hgap, int vgap, int minWidth) {
m_hgap = hgap;
m_vgap = vgap;
m_minWidth = minWidth;
}
public LogicalGridLayoutInfo getInfo() {
return m_info;
}
@Override
public int computeMinimumWidth(Composite parent, boolean changed) {
return computeMinimumSize(parent, changed).x;
}
public Point computeMinimumSize(Composite parent, boolean changed) {
return computeSize(parent, changed, SWT.DEFAULT, SWT.DEFAULT, MIN);
}
@Override
protected Point computeSize(Composite composite, int wHint, int hHint, boolean flushCache) {
return computeSize(composite, flushCache, wHint, hHint, PREF);
}
@Override
public int computeMaximumWidth(Composite parent, boolean changed) {
return computeSize(parent, changed, SWT.DEFAULT, SWT.DEFAULT, MAX).x;
}
public Point computeSize(Composite composite, boolean changed, int wHint, int hHint, int sizeFlag) {
if (changed) {
m_info = null;
}
if (wHint <= 0) {
wHint = SWT.DEFAULT;
}
validateLayout(composite, wHint, changed);
Point min = new Point(0, 0);
Point pref = new Point(0, 0);
Point max = new Point(0, 0);
// w
int useCount = 0;
for (int i = 0; i < m_info.cols; i++) {
if (useCount > 0) {
min.x = min.x + m_hgap;
pref.x = pref.x + m_hgap;
max.x = max.x + m_hgap;
}
min.x = min.x + m_info.width[i][MIN];
pref.x = pref.x + m_info.width[i][PREF];
max.x = max.x + m_info.width[i][MAX];
useCount++;
}
// h
useCount = 0;
for (int i = 0; i < m_info.rows; i++) {
if (useCount > 0) {
min.y = min.y + m_vgap;
pref.y = pref.y + m_vgap;
max.y = max.y + m_vgap;
}
min.y = min.y + m_info.height[i][MIN];
pref.y = pref.y + m_info.height[i][PREF];
max.y = max.y + m_info.height[i][MAX];
useCount++;
}
Point size = new Point(0, 0);
switch (sizeFlag) {
case MIN:
if (m_minWidth < 0) {
size.x = min.x;
}
else {
size.x = m_minWidth;
}
size.y = min.y;
break;
case MAX:
size.x = min.x;
size.y = min.y;
break;
default:
// adjust width
if (wHint == SWT.DEFAULT) {
size.x = pref.x;
}
else {
size.x = wHint;
size.x = Math.min(max.x, size.x);
size.x = Math.max(min.x, size.x);
}
// adjust height
if (hHint == SWT.DEFAULT) {
size.y = pref.y;
}
else {
size.y = hHint;
size.y = Math.min(max.y, size.y);
size.y = Math.max(min.y, size.y);
}
break;
}
return size;
}
@Override
protected void layout(Composite parent, boolean flushCache) {
if (flushCache) {
m_info = null;
}
Rectangle clientArea = parent.getClientArea();
validateLayout(parent, clientArea.width, flushCache);
Point size = new Point(clientArea.width, clientArea.height);
Rectangle[][] cellBounds = m_info.layoutCellBounds(size);
if (m_debug || LOG.isDebugEnabled()) {
dumpLayoutInfo(parent);
}
// bounds
int n = m_info.components.length;
for (int i = 0; i < n; i++) {
Control comp = m_info.components[i];
LogicalGridData data = m_info.gridDatas[i];
Rectangle r1 = cellBounds[data.gridy][data.gridx];
Rectangle r2 = cellBounds[data.gridy + data.gridh - 1][data.gridx + data.gridw - 1];
Rectangle r = r1.union(r2);
if (data.topInset > 0) {
r.y += data.topInset;
r.height -= data.topInset;
}
if (data.fillHorizontal && data.fillVertical) {
// ok
}
else {
Point d = new Point(m_info.componentWidths[i], m_info.componentHeights[i]);
if (!data.fillHorizontal) {
if (d.x < r.width) {
int delta = r.width - d.x;
r.width = d.x;
if (data.horizontalAlignment == 0) {
r.x += delta / 2;
}
else if (data.horizontalAlignment > 0) {
r.x += delta;
}
}
}
if (!data.fillVertical) {
if (d.y < r.height) {
int delta = r.height - d.y;
if (data.heightHint == 0) {
r.height = d.y;
}
else {
r.height = data.heightHint;
}
if (data.verticalAlignment == 0) {
r.y += delta / 2;
}
else if (data.verticalAlignment > 0) {
r.y += delta;
}
}
}
}
comp.setBounds(r);
}
}
public void setDebug(boolean b) {
m_debug = b;
}
public void dumpLayoutInfo(Composite parent) {
dumpLayoutInfo(parent, new PrintWriter(System.out));
}
public void dumpLayoutInfo(Composite parent, PrintWriter out) {
Point parentSize = parent.getSize();
Rectangle[][] cellBounds = m_info.layoutCellBounds(parentSize);
Object field = SwtScoutComposite.getScoutModelOnWidget(parent);
String className = "undefined (PROP_SCOUT_OBJECT not set!)";
if (field != null) {
className = field.getClass().getSimpleName();
}
out.println("DUMP layout of: " + className + " compSize= " + parentSize);
out.println(" containerBounds = " + parent.getClientArea());
out.println(" Fields ---");
for (int i = 0; i < m_info.components.length; i++) {
Control c = m_info.components[i];
LogicalGridData data = m_info.gridDatas[i];
try {
Rectangle r1 = cellBounds[data.gridy][data.gridx];
Rectangle r2 = cellBounds[data.gridy + data.gridh - 1][data.gridx + data.gridw - 1];
Rectangle r = r1.union(r2);
String scoutObjectName = "NOT DEFINED";
Object scoutObject = SwtScoutComposite.getScoutModelOnWidget(c);
if (scoutObject != null) {
scoutObjectName = scoutObject.getClass().getSimpleName();
}
out.println(" b=" + r + " " + scoutObjectName);
if (scoutObject instanceof IFormField) {
out.println(" model grid: " + ((IFormField) scoutObject).getGridData().toString());
}
out.println(" uiGridData: " + data.toString());
}
catch (ArrayIndexOutOfBoundsException e) {
out.print("unable to print layout info of: " + data);
e.printStackTrace();
}
}
out.flush();
}
protected void validateLayout(Composite parent, int wHint, boolean flushCache) {
CompositeObject newKey = new CompositeObject(parent.getSize(), wHint == SWT.DEFAULT ? (m_infoCacheKey != null ? m_infoCacheKey.getComponent(1) : wHint) : wHint);
//check cache key
if (m_info != null) {
if (m_infoCacheKey == null || !m_infoCacheKey.equals(newKey)) {
m_info = null;
m_infoCacheKey = null;
}
}
if (m_info != null && m_infoCacheKey != null) {
return;
}
List<Control> visibleComps = new ArrayList<>();
List<LogicalGridData> visibleCons = new ArrayList<>();
for (Control comp : parent.getChildren()) {
if (comp.getVisible() && comp.getLayoutData() instanceof LogicalGridData) {
visibleComps.add(comp);
LogicalGridData cons = (LogicalGridData) comp.getLayoutData();
cons.validate();
visibleCons.add(cons);
}
}
m_info = new LogicalGridLayoutInfo(visibleComps.toArray(new Control[visibleComps.size()]), visibleCons.toArray(new LogicalGridData[visibleCons.size()]), m_hgap, m_vgap, wHint, flushCache);
m_infoCacheKey = newKey;
}
}