Last active
May 7, 2021 07:58
-
-
Save fappel/9168399 to your computer and use it in GitHub Desktop.
Draft of an Custom SWT Layout for Responsive SWT UI Development (depends on SWT and Google Guava)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import org.eclipse.swt.SWT; | |
import org.eclipse.swt.graphics.Color; | |
import org.eclipse.swt.layout.FillLayout; | |
import org.eclipse.swt.widgets.Composite; | |
import org.eclipse.swt.widgets.Control; | |
import org.eclipse.swt.widgets.Display; | |
import org.eclipse.swt.widgets.Label; | |
import org.eclipse.swt.widgets.Shell; | |
public class Demo { | |
public static void main(String[] args) { | |
Display display = new Display(); | |
Shell shell = new Shell( display, SWT.SHELL_TRIM ); | |
shell.setLayout( new FillLayout() ); | |
shell.setBounds( 20, 20, 1200, 600 ); | |
Composite parent = new Composite( shell, SWT.NONE ); | |
parent.setLayout( new ScaleLayout( new ScaleProvider( parent ) ) ); | |
parent.setBackground( getSystemColor( SWT.COLOR_LIST_BACKGROUND ) ); | |
configure( parent ); | |
shell.open(); | |
while( !shell.isDisposed() ) { | |
if( !shell.getDisplay().readAndDispatch() ) { | |
shell.getDisplay().sleep(); | |
} | |
} | |
} | |
private static void configure( Composite parent ) { | |
ScaleData child1 = createChild( parent, "control1", SWT.COLOR_RED ); | |
ScaleData child2 = createChild( parent, "control2", SWT.COLOR_GREEN ); | |
ScaleData child3 = createChild( parent, "control3", SWT.COLOR_BLUE ); | |
ScaleData child4 = createChild( parent, "control4", SWT.COLOR_CYAN ); | |
ScaleData child5 = createChild( parent, "control5", SWT.COLOR_YELLOW ); | |
child1.on( Scale.SUPER_WIDE ).setMargin( 3, 3, 3, 3 ); | |
child2.on( Scale.SUPER_WIDE ).setMargin( 3, 3, 3, 3 ); | |
child3.on( Scale.SUPER_WIDE ).setWidth( 300 ).setMargin( 3, 3, 3, 3 ); | |
child4.on( Scale.SUPER_WIDE ).setMargin( 3, 3, 3, 3 ); | |
child5.on( Scale.SUPER_WIDE ).setMargin( 3, 3, 3, 3 ); | |
child1.on( Scale.WIDE ).setMargin( 3, 3, 3, 3 ); | |
child2.on( Scale.WIDE ).setMargin( 3, 3, 3, 3 ); | |
child3.on( Scale.WIDE ).setWidth( 300 ).setMargin( 3, 3, 3, 3 ); | |
child4.on( Scale.WIDE ).tie( child5 ).setHeight( 200 ).setMargin( 3, 3, 3, 3 ); | |
child5.on( Scale.WIDE ).setMargin( 3, 3, 3, 3 ); | |
child1.on( Scale.STANDARD ).setMargin( 3, 3, 3, 3 ); | |
child2.on( Scale.STANDARD ).tie( child3 ).setMargin( 3, 3, 3, 3 ); | |
child3.on( Scale.STANDARD ).setWidth( 300 ).setMargin( 3, 3, 3, 3 ); | |
child4.on( Scale.STANDARD ).tie( child5 ).setMargin( 3, 3, 3, 3 ); | |
child5.on( Scale.STANDARD ).setMargin( 3, 3, 3, 3 ); | |
child1.on( Scale.COMPACT ).setMargin( 3, 3, 3, 3 ); | |
child2.on( Scale.COMPACT ).tie( child3 ).setMargin( 3, 3, 3, 3 ); | |
child3.on( Scale.COMPACT ).tie( child4 ).setSize( 300, 60 ).setMargin( 3, 3, 3, 3 ); | |
child4.on( Scale.COMPACT ).tie( child5 ).setMargin( 3, 3, 3, 3 ); | |
child5.on( Scale.COMPACT ).setMargin( 3, 3, 3, 3 ); | |
child1.on( Scale.SUPER_COMPACT ).tie( child2 ).setMargin( 3, 3, 3, 3 ); | |
child2.on( Scale.SUPER_COMPACT ).tie( child3 ).setMargin( 3, 3, 3, 3 ); | |
child3.on( Scale.SUPER_COMPACT ).tie( child4 ).setHeight( 60 ).setMargin( 3, 3, 3, 3 ); | |
child4.on( Scale.SUPER_COMPACT ).tie( child5 ).setMargin( 3, 3, 3, 3 ); | |
child5.on( Scale.SUPER_COMPACT ).setMargin( 3, 3, 3, 3 ); | |
} | |
static ScaleData createChild( Composite parent, String label, int color ) { | |
return new ScaleData( createChildControl( parent, label, color ) ); | |
} | |
private static Control createChildControl( Composite parent, String label, int color ) { | |
Label result = new Label( parent, SWT.WRAP ); | |
result.setText( label ); | |
result.setBackground( getSystemColor( color ) ); | |
return result; | |
} | |
private static Color getSystemColor( int colorIndex ) { | |
return Display.getCurrent().getSystemColor( colorIndex ); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public enum Scale { | |
SUPER_WIDE, WIDE, STANDARD, COMPACT, SUPER_COMPACT; | |
static final int UPPER_BOUND_SUPER_COMPACT = 480; | |
static final int UPPER_BOUND_COMPACT = 640; | |
static final int UPPER_BOUND_STANDARD = 800; | |
static final int UPPER_BOUND_WIDE = 1024; | |
public static Scale valueOf( int width ) { | |
Scale result = Scale.SUPER_WIDE; | |
if( width < UPPER_BOUND_SUPER_COMPACT ) { | |
result = SUPER_COMPACT; | |
} else if( width < UPPER_BOUND_COMPACT ) { | |
result = COMPACT; | |
} else if( width < UPPER_BOUND_STANDARD ) { | |
result = STANDARD; | |
} else if( width < UPPER_BOUND_WIDE ) { | |
result = WIDE; | |
} | |
return result; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import static com.google.common.collect.Maps.newHashMap; | |
import java.util.HashMap; | |
import org.eclipse.swt.widgets.Control; | |
public class ScaleData { | |
private final HashMap<Scale, ScaleDetail> scales; | |
private final Control control; | |
public ScaleData( Control control ) { | |
this.scales = newHashMap(); | |
this.control = control; | |
control.setLayoutData( this ); | |
} | |
public Control getControl() { | |
return control; | |
} | |
public ScaleDetail on( Scale scale ) { | |
if( !scales.containsKey( scale ) ) { | |
scales.put( scale, new ScaleDetail( this, scale ) ); | |
} | |
return scales.get( scale ); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import org.eclipse.swt.SWT; | |
import org.eclipse.swt.graphics.Rectangle; | |
public class ScaleDetail { | |
private final ScaleData current; | |
private final Scale scale; | |
private ScaleData predecessor; | |
private ScaleData successor; | |
private Rectangle margin; | |
private int width; | |
private int height; | |
ScaleDetail( ScaleData current, Scale scale ) { | |
this.current = current; | |
this.scale = scale; | |
this.width = SWT.DEFAULT; | |
this.height = SWT.DEFAULT; | |
this.margin = new Rectangle( 0, 0, 0, 0 ); | |
} | |
public int getWidth() { | |
return width; | |
} | |
public ScaleDetail setWidth( int width ) { | |
this.width = width; | |
return this; | |
} | |
public int getHeight() { | |
return height; | |
} | |
public ScaleDetail setHeight( int height ) { | |
this.height = height; | |
return this; | |
} | |
public ScaleDetail setSize( int width, int height ) { | |
setWidth( width ); | |
setHeight( height ); | |
return this; | |
} | |
public Rectangle getMargin() { | |
return margin; | |
} | |
public ScaleDetail setMargin( int left, int top, int right, int bottom ) { | |
this.margin = new Rectangle( left, top, right, bottom ); | |
return this; | |
} | |
public ScaleDetail tie( ScaleData successor ) { | |
if( this.successor != null ) { | |
this.successor.on( scale ).setPredecessor( null ); | |
} | |
this.successor = successor; | |
if( successor != null ) { | |
successor.on( scale ).setPredecessor( current ); | |
} | |
return this; | |
} | |
public ScaleData getSuccessor() { | |
return successor; | |
} | |
public boolean hasSuccessor() { | |
return successor != null; | |
} | |
boolean hasPredecessor() { | |
return predecessor != null; | |
} | |
private void setPredecessor( ScaleData predecessor ) { | |
this.predecessor = predecessor; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import org.eclipse.swt.widgets.Composite; | |
public class ScaleEvent { | |
private final Composite source; | |
private final Scale newScale; | |
private final Scale oldScale; | |
ScaleEvent( Composite source, Scale newScale, Scale oldScale ) { | |
this.source = source; | |
this.newScale = newScale; | |
this.oldScale = oldScale; | |
} | |
public Composite getSource() { | |
return source; | |
} | |
public Scale getNewScale() { | |
return newScale; | |
} | |
public Scale getOldScale() { | |
return oldScale; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import static com.google.common.base.Preconditions.checkArgument; | |
import static com.google.common.collect.Lists.newLinkedList; | |
import static java.lang.Math.max; | |
import java.util.List; | |
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; | |
public class ScaleLayout extends Layout { | |
private final ScaleProvider scaleProvider; | |
public ScaleLayout( ScaleProvider scaleProvider ) { | |
checkArgument( scaleProvider != null, "Parameter 'scaleProvider must not be null." ); | |
this.scaleProvider = scaleProvider; | |
} | |
public ScaleProvider getScaleProvider() { | |
return scaleProvider; | |
} | |
@Override | |
protected Point computeSize( Composite composite, int wHint, int hHint, boolean flushCache ) { | |
Scale scale = scaleProvider.getScale(); | |
Point result = newSize( 0, 0, wHint, hHint ); | |
for( ScaleData columnHead : getColumnHeads( scale, composite ) ) { | |
Point columnSize = computeColumnSize( scale, columnHead ); | |
result = new Point( result.x + columnSize.x, max( result.y, columnSize.y ) ); | |
} | |
return newSize( result.x, result.y, wHint, hHint ); | |
} | |
private static Point newSize( int width, int height, int wHint, int hHint ) { | |
return new Point( wHint == SWT.DEFAULT ? width : wHint, hHint == SWT.DEFAULT ? height : hHint ); | |
} | |
private static Point computeColumnSize( Scale scale, ScaleData columnHead ) { | |
Point result = new Point( 0, 0 ); | |
for( ScaleData element : getTiedElements( scale, columnHead ) ) { | |
Point size = computeSize( scale, element ); | |
Rectangle margin = element.on( scale ).getMargin(); | |
int x = max( result.x, size.x + margin.x + margin.width ); | |
int y = result.y + size.y + margin.y + margin.height; | |
result = new Point( x, y ); | |
} | |
return result; | |
} | |
@Override | |
protected void layout( Composite composite, boolean flushCache ) { | |
Scale scale = scaleProvider.getScale(); | |
int xPos = composite.getClientArea().x; | |
for( ScaleData columnHead : getColumnHeads( scale, composite ) ) { | |
int columnWidth = layoutColumn( scale, columnHead, composite, xPos ); | |
xPos += columnWidth; | |
} | |
} | |
private static int layoutColumn( Scale scale, ScaleData head, Composite composite, int xPos ) { | |
int result = calculateColumnWidth( scale, head ); | |
int yPos = composite.getClientArea().y; | |
for( ScaleData element : getTiedElements( scale, head ) ) { | |
Rectangle bounds = calculateElementBounds( scale, element, xPos, yPos, result ); | |
bounds.height = adjustHeightIfBottomAttached( scale, element, bounds, head, composite ); | |
bounds.width = adjustWidthIfRightAttached( scale, element, bounds, head, composite ); | |
element.getControl().setBounds( bounds ); | |
Rectangle margin = element.on( scale ).getMargin(); | |
yPos += bounds.height + margin.y + margin.height; | |
} | |
return result; | |
} | |
private static Rectangle calculateElementBounds( | |
Scale scale, ScaleData element, int x, int y, int width ) | |
{ | |
int height = computeSize( scale, element ).y; | |
Rectangle margin = element.on( scale ).getMargin(); | |
return new Rectangle( x + margin.x, y + margin.y, width - margin.x - margin.width, height ); | |
} | |
private static int adjustHeightIfBottomAttached( | |
Scale scale, ScaleData element, Rectangle bounds, ScaleData columnHead, Composite composite ) | |
{ | |
int result = bounds.height; | |
List<ScaleData> elements = getTiedElements( scale, columnHead ); | |
if( isLastElementInList( element, elements ) && !hasHeightHint( scale, element ) ) { | |
Rectangle margin = element.on( scale ).getMargin(); | |
result = composite.getClientArea().height - bounds.y - margin.height; | |
} | |
return result; | |
} | |
private static int adjustWidthIfRightAttached( | |
Scale scale, ScaleData element, Rectangle bounds, ScaleData head, Composite composite ) | |
{ | |
int result = bounds.width; | |
List<ScaleData> columnHeads = getColumnHeads( scale, composite ); | |
if( isLastElementInList( head, columnHeads ) && !hasWidthHint( scale, element ) ) { | |
Rectangle margin = element.on( scale ).getMargin(); | |
result = composite.getClientArea().width - bounds.x - margin.width; | |
} | |
return result; | |
} | |
private static List<ScaleData> getColumnHeads( Scale scale, Composite composite ) { | |
List<ScaleData> result = newLinkedList(); | |
for( Control control : composite.getChildren() ) { | |
ScaleData data = ( ScaleData )control.getLayoutData(); | |
if( data != null && !data.on( scale ).hasPredecessor() ) { | |
result.add( data ); | |
} | |
} | |
return result; | |
} | |
private static int calculateColumnWidth( Scale scale, ScaleData head ) { | |
int result = 0; | |
for( ScaleData element : getTiedElements( scale, head ) ) { | |
Rectangle margin = element.on( scale ).getMargin(); | |
result = max( result, computeSize( scale, element ).x + margin.x + margin.width ); | |
} | |
return result; | |
} | |
private static List<ScaleData> getTiedElements( Scale scale, ScaleData firstElement ) { | |
List<ScaleData> result = newLinkedList(); | |
result.add( firstElement ); | |
if( firstElement.on( scale ).hasSuccessor() ) { | |
ScaleData successor = firstElement.on( scale ).getSuccessor(); | |
result.addAll( getTiedElements( scale, successor ) ); | |
} | |
return result; | |
} | |
private static Point computeSize( Scale scale, ScaleData element ) { | |
ScaleDetail detail = element.on( scale ); | |
Control control = element.getControl(); | |
return control.computeSize( detail.getWidth(), detail.getHeight(), true ); | |
} | |
private static boolean isLastElementInList( ScaleData element, List<ScaleData> elements ) { | |
return element == elements.get( elements.size() - 1 ); | |
} | |
private static boolean hasHeightHint( Scale scale, ScaleData element ) { | |
return element.on( scale ).getHeight() != SWT.DEFAULT; | |
} | |
private static boolean hasWidthHint( Scale scale, ScaleData element ) { | |
return element.on( scale ).getWidth() != SWT.DEFAULT; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public interface ScaleListener { | |
void scaleChanged( ScaleEvent event ); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import static com.google.common.base.Preconditions.checkArgument; | |
import static com.google.common.collect.Iterables.toArray; | |
import java.util.Collection; | |
import org.eclipse.swt.widgets.Composite; | |
import com.google.common.collect.Sets; | |
public class ScaleProvider { | |
final Composite composite; | |
final Collection<ScaleListener> listeners; | |
Scale scale; | |
public ScaleProvider( Composite composite ) { | |
checkArgument( composite != null, "Parameter 'composite' must not null." ); | |
this.listeners = Sets.newHashSet(); | |
this.composite = composite; | |
} | |
public Scale getScale() { | |
Scale oldScale = scale; | |
scale = Scale.valueOf( composite.getSize().x ); | |
if( scale != oldScale ) { | |
notifyListeners( composite, scale, oldScale ); | |
} | |
return scale; | |
} | |
public void addScaleListener( ScaleListener listener ) { | |
listeners.add( listener ); | |
} | |
public void removeScaleListener( ScaleListener listener ) { | |
listeners.remove( listener ); | |
} | |
private void notifyListeners( Composite composite, Scale newScale, Scale oldScale ) { | |
ScaleEvent event = new ScaleEvent( composite, newScale, oldScale ); | |
for( ScaleListener scaleListener : toArray( listeners, ScaleListener.class ) ) { | |
scaleListener.scaleChanged( event ); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment