Dynamic font size - java

how do I calculate the font size of a TextView so that always occupy a certain percentage of the screen?
now I spit it out:
.....
densityNormal = Float.parseFloat("1.0");
densitySub = Float.parseFloat("0.75");
densityIper = Float.parseFloat("1.5");
densityExtra = Float.parseFloat("2.0");
.....
textviewSample.setTextSize(getHeightPercentage(context,30));
.....
private float getHeightPercentage(Context context, int percentage) {
if (percentage > 0 && percentage < 100) {
float appHeight = context.getApplicationContext().getResources()
.getDisplayMetrics().heightPixels;
float appSPDensity = context.getApplicationContext().getResources()
.getDisplayMetrics().scaledDensity;
if (Float.compare(appSPDensity, densitySub) == 0) {
appHeight = appHeight + ((appHeight * 25) / 100);
} else if (Float.compare(appSPDensity, densityIper) == 0) {
appHeight = ((appHeight * 50) / 100) - appHeight;
} else if (Float.compare(appSPDensity, densityExtra) == 0) {
appHeight = ((appHeight * 200) / 100) - appHeight;
}
return new Float((appHeight * percentage) / 100);
} else
return -1;
}
but I can not integrate the different density of the screen,have you any suggestions?8y3

Related

Moving a Object in a specified road

I am trying to make this spaceship follow a certain road and when it hits a x and a y point of the screen that I specified its rotation is changed but it seems that the spaceship doesn't follow this road.
enter code here
private void menuSideShipSetup() {
menushiptypes = 2;
arrivaltime = 4 * 20;
Sideship = new Bitmap[menushiptypes];
Sideship[0] = Utils.loadImage(root,"animasyon/ship_1_1.png" );
Sideship[1] = Utils.loadImage(root,"animasyon/ship_2_1.png" );
sideshipx = 0;
sideshipy = 1920 / 2 - Sideship [0].getHeight() /2;
rightsideshipx = 880;
rightsideshipy = 1920 / 2 - Sideship[1].getHeight() /2;
sideshipspeed = 1080 / 80;
rotateconstantly += (135 - 45) / arrivaltime;
sideshiprotation = -1;
rightsideshiprotation = 1;
rightsideshiprotationy = 1;
private void RightShipMovement() {
if ( rightsideshipx >= 880 || rightsideshipx <=0 ){
rightsideshiprotation = rightsideshiprotation * -1;
}
rightsideshipx += sideshipspeed * rightsideshiprotation;
rotateconstantly += (135 - 45) / arrivaltime;
}
enter code here
private void rightVerticalMovement(){
if ((rightsideshipy >= 1260 )){
rightsideshiprotationy = rightsideshiprotationy * -1;
}
rightsideshipy += (300/35) * rightsideshiprotationy;
}
private void rightVertical2Movement(){
if ((rightsideshipy <= 320)){
rightsideshiprotationy = rightsideshiprotationy * -1;
}
rightsideshipy += (300/35) * rightsideshiprotationy;
}
enter code here
}
enter code here

convert a dynamic circle layout to oval or ellipse in android

I have a image group with three images perpendicularly, those group forms a circle, i have created it as circle view couldn't adjust its width to make it oval, since i have three stacked images one above one i couldn't adjust all those images the code as below's
static int Standard_height = 160;
static int Cam_Size = 50;
static int Segment_Size = 55;
static int TextSize = 10;
private void SetLayoutDesign() {
Layout_Frame.removeAllViews();
Segments = sharedpreferences.getInt(Vars.Selected_Segment_Count, 4);
int imageGap = Segment_Size + 5;
int size = Segment_Size + ((16 - (Segments + 1)));
int imageSize = Cam_Size - Segments;
for (int i = 0; i < Segments; i++) {
// Create some quick TextViews that can be placed.
String StateName = Vars.Segment_State + "_" + i;
int state = sharedpreferences.getInt(StateName, 0);
String TypeName = Vars.Segment_Type + "_" + i;
int type = sharedpreferences.getInt(TypeName, 0);
Segment_Button[i] = new ImageButton(act);
Segment_Name[i] = new TextView(act);
if (type == 0) {
Segment_Name[i].setText((i + 1) + "");
if (state == 1) {
Segment_Button[i].setBackgroundResource(R.drawable.sort_none);
} else {
Segment_Button[i].setBackgroundResource(R.drawable.sort_off);
}
} else if (type == 1) {
Segment_Name[i].setText((i + 1) + "\nMS");
if (state == 1) {
Segment_Button[i].setBackgroundResource(R.drawable.sort_ms);
} else {
Segment_Button[i].setBackgroundResource(R.drawable.sort_off);
}
} else if (type == 2) {
Segment_Name[i].setText((i + 1) + "\nFR");
if (state == 1) {
Segment_Button[i].setBackgroundResource(R.drawable.sort_fr);
} else {
Segment_Button[i].setBackgroundResource(R.drawable.sort_off);
}
} else if (type == 3) {
Segment_Name[i].setText((i + 1) + "\nSR");
if (state == 1) {
Segment_Button[i].setBackgroundResource(R.drawable.sort_sr);
} else {
Segment_Button[i].setBackgroundResource(R.drawable.sort_off);
}
}
Segment_Name[i].setGravity(Gravity.CENTER);
Segment_Name[i].setTextColor(Color.WHITE);
Segment_Name[i].setTextSize(TextSize);
Segment_Name[i].setId(Segment_Text_Ids[i]);
Segment_Name[i].setTypeface(null, Typeface.BOLD);
Segment_Button[i].setId(Segment_Ids[i]);
FrameLayout.LayoutParams lp1 = new FrameLayout.LayoutParams(imageSize, imageSize);
FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(size, size);
lp.gravity = Gravity.CENTER;
lp1.gravity = Gravity.CENTER;
Segment_Button[i].setLayoutParams(lp);
Segment_Button[i].setOnClickListener(this);
Segment_Button[i].setOnLongClickListener(this);
Segment_Name[i].setLayoutParams(lp);
float angleDeg = i * 360.0f / Segments - 90.0f;
float angleRad = (float) (angleDeg * Math.PI / 180.0f);
Segment_Button[i].setTranslationX(Standard_height * (float) Math.cos(angleRad));
Segment_Button[i].setTranslationY(Standard_height * (float) Math.sin(angleRad));
Layout_Frame.addView(Segment_Button[i]);
Segment_Name[i].setTranslationX(Standard_height * (float) Math.cos(angleRad));
Segment_Name[i].setTranslationY(Standard_height * (float) Math.sin(angleRad));
if (Segments % 2 == 0 && Segments != 0) {
Segment_Name[i].setRotation((180 / Segments));
} else {
Segment_Name[i].setRotation(0);
}
Layout_Frame.addView(Segment_Name[i]);
FCam_Button[i] = new ImageButton(act);
FCam_Button[i].setId(FCam_Ids[i]);
String FCamName = Vars.FCam_State + "_" + i;
int FCamState = sharedpreferences.getInt(FCamName, 0);
if (FCamState == 0) {
FCam_Button[i].setBackgroundResource(R.drawable.f_camera_off_image);
} else {
FCam_Button[i].setBackgroundResource(R.drawable.f_camera_on_image);
}
FCam_Button[i].setLayoutParams(lp1);
FCam_Button[i].setOnClickListener(this);
FCam_Button[i].setTranslationX((Standard_height + imageGap) * (float) Math.cos(angleRad));
FCam_Button[i].setTranslationY((Standard_height + imageGap) * (float) Math.sin(angleRad));
FCam_Button[i].setRotation(angleDeg + 90.0f);
Layout_Frame.addView(FCam_Button[i]);
RCam_Button[i] = new ImageButton(act);
RCam_Button[i].setId(RCam_Ids[i]);
String RCamName = Vars.RCam_State + "_" + i;
int RCamState = sharedpreferences.getInt(RCamName, 0);
if (RCamState == 0) {
RCam_Button[i].setBackgroundResource(R.drawable.r_camera_off_image);
} else {
RCam_Button[i].setBackgroundResource(R.drawable.r_camera_on_image);
}
RCam_Button[i].setLayoutParams(lp1);
RCam_Button[i].setOnClickListener(this);
RCam_Button[i].setTranslationX((Standard_height - imageGap) * (float) Math.cos(angleRad));
RCam_Button[i].setTranslationY((Standard_height - imageGap) * (float) Math.sin(angleRad));
RCam_Button[i].setRotation(angleDeg + 90.0f);
Layout_Frame.addView(RCam_Button[i]);
}
if (Segments % 2 == 0 && Segments != 0) {
Layout_Frame.setRotation(-(180 / Segments));
} else {
Layout_Frame.setRotation(0);
}
SetUserLevelControls();
}
This is the current layout
This is the desired layout

Game rope swing physics acting weird

I'm trying to implement rope swinging in my platformer, following this tutorial. Instead of swing on the rope, the player looks like he's sliding down a slope: he moves very slowly towards the bottom.
This is what it looks like now:
Instead, I want the player to have more natural movement, like he's really swinging on the rope.
This is the update method from my player class:
#Override
public final void update() {
setPosition(getNextPosition());
if (direction == Direction.LEFT && moving) {
getVelocity().x = -WALK_SPEED;
} else if (getVelocity().x < 0) {
getVelocity().x *= COEF_FRIC;
}
if (direction == Direction.RIGHT && moving) {
getVelocity().x = WALK_SPEED;
} else if (getVelocity().x > 0) {
getVelocity().x *= COEF_FRIC;
}
checkAsleep();
animations.update();
if (ropePoint != null) {
//getCenter() returns the center position of the player
if (getCenter().toPoint().distanceSq(ropePoint) > ROPE_LENGTH * ROPE_LENGTH) {
final Vec2D oldPosition = getCenter();
final Vec2D oldVelocity = getVelocity();
final Vec2D ropePosition = new Vec2D(ropePoint);
setCenter((oldPosition.subtract(ropePosition).unit().multiply(ROPE_LENGTH).add(ropePosition)));
setVelocity(oldPosition.subtract(getCenter()).unit().multiply(oldVelocity));
}
}
}
This is my implementation of getNextPosition(), if it is needed.
public final Vec2D getNextPosition() {
final int currCol = (int) (getX() / Tile.SIZE);
final int currRow = (int) (getY() / Tile.SIZE);
final double destX = getX() + moveData.velocity.x;
final double destY = getY() + moveData.velocity.y;
double tempX = getX();
double tempY = getY();
Corners solidCorners = getCornersAreSolid(getX(), destY);
boolean topLeft = solidCorners.topLeft;
boolean topRight = solidCorners.topRight;
boolean bottomLeft = solidCorners.bottomLeft;
boolean bottomRight = solidCorners.bottomRight;
framesSinceLastTopCollision += 1;
framesSinceLastBottomCollision += 1;
framesSinceLastLeftCollision += 1;
framesSinceLastRightCollision += 1;
if (moveData.velocity.y < 0) {
if (topLeft || topRight) {
moveData.velocity.y = 0;
tempY = currRow * Tile.SIZE;
framesSinceLastTopCollision = 0;
} else {
tempY += moveData.velocity.y;
}
} else if (moveData.velocity.y > 0) {
if (bottomLeft || bottomRight) {
moveData.velocity.y = 0;
tempY = (currRow + 1) * Tile.SIZE - moveData.collisionBox.getHeight() % Tile.SIZE - 1;
framesSinceLastBottomCollision = 0;
} else {
tempY += moveData.velocity.y;
}
}
solidCorners = getCornersAreSolid(destX, getY());
topLeft = solidCorners.topLeft;
topRight = solidCorners.topRight;
bottomLeft = solidCorners.bottomLeft;
bottomRight = solidCorners.bottomRight;
if (moveData.velocity.x < 0) {
if (topLeft || bottomLeft) {
moveData.velocity.x = 0;
tempX = currCol * Tile.SIZE;
framesSinceLastLeftCollision = 0;
} else {
tempX += moveData.velocity.x;
}
}
if (moveData.velocity.x > 0) {
if (topRight || bottomRight) {
moveData.velocity.x = 0;
tempX = (currCol + 1) * Tile.SIZE - moveData.collisionBox.getWidth() % Tile.SIZE - 1;
framesSinceLastRightCollision = 0;
} else {
tempX += moveData.velocity.x;
}
}
return new Vec2D(tempX, tempY);
}
What should I change in this code to get natural movement?
My first guess is that the problem lies in that first if statement:
if (direction == Direction.LEFT && moving) {
getVelocity().x = -WALK_SPEED;
} else if (getVelocity().x < 0) {
getVelocity().x *= COEF_FRIC;
}
If the first thing is true, you're going to constantly be setting the velocity to walking pace, which doesn't make sense when your guy is swinging on a rope. He should be speeding up as he goes down and slowing down on the way up.
If the first thing is false, then since he is going left, you're definitely going to go into the else if statement, and he'll be slowed down by friction. I don't see where you set that, but it seems to still be the friction he has on the ground, which would seem to explain why he's all stuttery and looks more like he's sliding than falling.
You might want to add different states instead of just "moving", (perhaps jumping, swinging, walking, running, stopped) and vary how he behaves while doing each of those things.

Simple octree traversal: How to get the intersection points with AABB

This is my first question here on stackoverflow, so i hope i got all things right. For an java project i need to use an octree for raytracing. I already created an simple octree (without neighbour information or something) and sorted the triangles of the object meshs into the AABB's of the octree. Now I would like to make an easy traversation through the tree for every ray. (it should be really easy because the time to finish this project is very short). The basic algorithmn is the following:
Start with the first node
if this node is hit, remember the place of the intersection in a sorted list
if this node has children check if the child boxes are hit and write every intersection point in a sorted list
start with the child box with the nearest intersection point
if this box has children too see 4)
if a node doesn't have any childs check every triangle in this
box against the ray
if a triangle is hit get the color of triangle (with shading and
everything) and draw it on the screen
Unfortunately my current implementation seems to have a "bug" in the intersection calculation (ray vs ABBB). I check if any side of the AABB is hit and remember the clostest ip (smallest distance from ray origin).
Here is the Code for this function in my BoundingBox Class:
public HitResult intersects6(Ray ray) {
double t;
Vec3d ip = new Vec3d();
HitResult finalHitResult = null;
// front xy
if (Math.abs(ray.direction.z) > Helper.EPSYLON) {
t = (vmax.z - ray.origin.z) / ray.direction.z;
ip.x = ray.origin.x + t * ray.direction.x;
ip.y = ray.origin.y + t * ray.direction.y;
ip.z = vmax.z;
if ((ip.x >= vmin.x) && (ip.x <= vmax.x) && (ip.y >= vmin.y) && (ip.y <= vmax.y)) {
// here is an intersection
double distance = Vec3d.distance(ray.origin, ip);
finalHitResult = new HitResult(ip, distance);
}
}
// back xy
if (Math.abs(ray.direction.z) > Helper.EPSYLON) {
t = (vmin.z + ray.origin.z) / -ray.direction.z;
ip.x = ray.origin.x + t * ray.direction.x;
ip.y = ray.origin.y + t * ray.direction.y;
ip.z = vmin.z;
if ((ip.x >= vmin.x) && (ip.x <= vmax.x) && (ip.y >= vmin.y) && (ip.y <= vmax.y)) {
double distance = Vec3d.distance(ray.origin, ip);
if (finalHitResult!= null) {
if(distance < finalHitResult.distance)
finalHitResult.distance = distance;
finalHitResult.point = ip;
}
else
finalHitResult = new HitResult(ip, distance);
}
}
// Side Right
if (Math.abs(ray.direction.x) > Helper.EPSYLON) {
t = (vmax.x - ray.origin.x) / ray.direction.x;
ip.y = ray.origin.y + t * ray.direction.y;
ip.z = ray.origin.z + t * ray.direction.z;
ip.x = vmax.x;
if ((ip.y >= vmin.y) && (ip.y <= vmax.y) && (ip.z >= vmin.z) && (ip.z <= vmax.z)) {
double distance = Vec3d.distance(ray.origin, ip);
if (finalHitResult!= null) {
if(distance < finalHitResult.distance)
finalHitResult.distance = distance;
finalHitResult.point = ip;
}
else
finalHitResult = new HitResult(ip, distance);
}
}
// Side Left
if (Math.abs(ray.direction.x) > Helper.EPSYLON) {
t = (vmin.x + ray.origin.x) / -ray.direction.x;
ip.y = ray.origin.y + t * ray.direction.y;
ip.z = ray.origin.z + t * ray.direction.z;
ip.x = vmin.x;
if ((ip.y >= vmin.y) && (ip.y <= vmax.y) && (ip.z >= vmin.z) && (ip.z <= vmax.z)) {
double distance = Vec3d.distance(ray.origin, ip);
if (finalHitResult!= null) {
if(distance < finalHitResult.distance)
finalHitResult.distance = distance;
finalHitResult.point = ip;
}
else
finalHitResult = new HitResult(ip, distance);
}
}
// Top
if (Math.abs(ray.direction.y) > Helper.EPSYLON) {
t = (vmax.y - ray.origin.y) / ray.direction.y;
ip.x = ray.origin.x + t * ray.direction.x;
ip.z = ray.origin.z + t * ray.direction.z;
ip.y = vmax.y;
if ((ip.x >= vmin.x) && (ip.x <= vmax.x) && (ip.z >= vmin.z) && (ip.z <= vmax.z)) {
double distance = Vec3d.distance(ray.origin, ip);
if (finalHitResult!= null) {
if(distance < finalHitResult.distance)
finalHitResult.distance = distance;
finalHitResult.point = ip;
}
else
finalHitResult = new HitResult(ip, distance);
}
}
// Bottom
if (Math.abs(ray.direction.y) > Helper.EPSYLON) {
t = (vmin.y + ray.origin.y) / -ray.direction.y;
ip.x = ray.origin.x + t * ray.direction.x;
ip.z = ray.origin.z + t * ray.direction.z;
ip.y = vmin.y;
if ((ip.x >= vmin.x) && (ip.x <= vmax.x) && (ip.z >= vmin.z) && (ip.z <= vmax.z)) {
double distance = Vec3d.distance(ray.origin, ip);
if (finalHitResult!= null) {
if(distance < finalHitResult.distance)
finalHitResult.distance = distance;
finalHitResult.point = ip;
}
else
finalHitResult = new HitResult(ip, distance);
}
}
return finalHitResult;
I guess this is not the best way to do it. In my first implementation i just used the t-values and compared them (to find the box i would like to visit next). But the problem was the same. Some intersections could not be found.
I also checked out the intersection method here:
https://code.google.com/p/3d-workspace/source/browse/trunk/MathLibrary/Bounding/BoundingBox.cpp?r=17
but I can't see how to get the intersection point with this code (or even any t-value). Moreover i tested the slab method like descripted here:
http://tavianator.com/2011/05/fast-branchless-raybounding-box-intersections/
but this seems to also miss some intersections, i don't know why:
public double[] intersects3(Ray ray) {
double Tnear = -1e30;
double Tfar = 1e30;
// First, check slab in X.
if (Math.abs(ray.direction.x) < 0.0) {
// Ray is parallel to X, but starts outside. Fail.
if (ray.origin.x < vmin.x || ray.origin.x > vmax.x) {
return null;
}
} else {
double Ta = ((vmin.x - ray.origin.x) / ray.direction.x), Tb = (vmax.x - ray.origin.x) / ray.direction.x;
double T1 = Math.min(Ta, Tb);
double T2 = Math.max(Ta, Tb);
if (T1 > Tnear)
Tnear = T1;
if (T2 < Tfar)
Tfar = T2;
if (Tnear > Tfar)
return null;
if (Tfar < 0)
return null;
}
// Then check slab in Y.
if (Math.abs(ray.direction.y) < 0.0) {
// Ray is parallel to X, but starts outside. Fail.
if (ray.origin.y < vmin.y || ray.origin.y > vmax.y) {
return null;
}
} else {
double Ta = (vmin.y - ray.origin.y) / ray.direction.y, Tb = (vmax.y - ray.origin.y) / ray.direction.y;
double T1 = Math.min(Ta, Tb);
double T2 = Math.max(Ta, Tb);
if (T1 > Tnear)
Tnear = T1;
if (T2 < Tfar)
Tfar = T2;
if (Tnear > Tfar)
return null;
if (Tfar < 0)
return null;
}
// Then check slab in Z.
if (Math.abs(ray.direction.z) < 0.0) {
// Ray is parallel to X, but starts outside. Fail.
if (ray.origin.z < vmin.z || ray.origin.z > vmax.z) {
return null;
}
} else {
double Ta = (vmin.z - ray.origin.z) / ray.direction.z, Tb = (vmax.z - ray.origin.z) / ray.direction.z;
double T1 = Math.min(Ta, Tb);
double T2 = Math.max(Ta, Tb);
if (T1 > Tnear)
Tnear = T1;
if (T2 < Tfar)
Tfar = T2;
if (Tnear > Tfar)
return null;
if (Tfar < 0)
return null;
}
// If we have survived this far, the test passed.
return new double[] { Tnear, Tfar };
}
Maybe I'm too stupid for raytracing at all.
But my actual question is:
Is it possible to use the t-values to compare which box has the closest intersection point ? And if yes, how can i get this t-values? Or what could i do to make the first code snipped work? (so far i would be happy with ANY working solution, even if this solution is very slow )
Thanks in advance.
Maybe this could be helpful:
http://chiranjivi.tripod.com/octrav.html
I've tried to implement the idea for a quadtree:
https://github.com/alexroat/quadtree-traversal

Constant Width SashForm

My application has a SashForm with two children. I want the left child to stay the same size when the window is resized. I want the same thing Eclipse does with the Package Explorer and the main editor. When you resize the window only the text editor changes size. However, the Package Explorer is still resizeable by the sash.
I tried using the following
sashForm.addControlListener(new ControlAdapter() {
#Override
public void controlResized(ControlEvent e) {
int width = sashForm.getClientArea().width;
int[] weights = sashForm.getWeights();
weights[1] = width - weights[0];
sashForm.setWeights(weights);
}
});
The problem is the width of the left size either shrinks to 0 or expands too much. It looks like the weights are updated before this is called or something.
If I set weights[0] equal to some constant it does what I want.
I managed to get an example running that should give you an idea how to solve your problem. The thing is, that the SashForm uses weights rather than pixels. So you have to compute the percentage the left child has to occupy based on the parent size and assign the rest to the right child.
In my code example, you can specify the width of the left child and set a minimal size for the right child, such that the SashForm will always show both.
private static final int MIN_WIDTH_LEFT = 100;
private static final int MIN_WIDTH_RIGHT = 50;
public static void main(String[] args)
{
Display display = new Display();
final Shell shell = new Shell(display);
shell.setText("StackOverflow");
shell.setLayout(new FillLayout());
final SashForm form = new SashForm(shell, SWT.HORIZONTAL);
Button button = new Button(form, SWT.PUSH);
button.setText("Left");
Button buttonR = new Button(form, SWT.PUSH);
buttonR.setText("Right");
form.setWeights(new int[] {1, 2});
shell.addListener(SWT.Resize, new Listener()
{
#Override
public void handleEvent(Event arg0)
{
int width = shell.getClientArea().width;
int[] weights = form.getWeights();
if(width >= MIN_WIDTH_LEFT + MIN_WIDTH_RIGHT)
{
weights[0] = 1000000 * MIN_WIDTH_LEFT / width;
weights[1] = 1000000 - weights[0];
}
else
{
weights[0] = 1000000 * MIN_WIDTH_LEFT / (MIN_WIDTH_LEFT + MIN_WIDTH_RIGHT);
weights[1] = 1000000 * MIN_WIDTH_RIGHT / (MIN_WIDTH_LEFT + MIN_WIDTH_RIGHT);
}
System.out.println(width + " " + Arrays.toString(weights));
form.setWeights(weights);
}
});
shell.pack();
shell.setSize(600, 400);
shell.open();
while (!shell.isDisposed())
{
if (!display.readAndDispatch())
display.sleep();
}
display.dispose();
}
This is what it looks like:
Startup:
After resizing:
When decreasing the window size until it is too small to show both minimal sizes:
As you can see in this case, the minimal size for the left child is ignored to still be able to show both childs.
This is the best solution I could come up with.
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.SashForm;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Shell;
public class Main {
private static int leftWidth, oldWeight;
public static void main(String[] args) {
Display display = new Display();
final Shell shell = new Shell(display);
shell.setText("StackOverflow");
shell.setLayout(new FillLayout());
shell.setSize(600, 400);
final SashForm form = new SashForm(shell, SWT.HORIZONTAL);
final Button button = new Button(form, SWT.PUSH);
button.setText("Left");
button.addListener(SWT.Resize, new Listener() {
#Override
public void handleEvent(Event arg0) {
int[] weights = form.getWeights();
// oldWeights is used to distinguish between a window resize and
// a sash move
if (oldWeight != weights[0]) {
System.out.println("Weights changed!");
oldWeight = weights[0];
leftWidth = (int) Math.round((double) form.getClientArea().width
* (double) weights[0]
/ (double) (weights[0] + weights[1]));
}
}
});
Button buttonR = new Button(form, SWT.PUSH);
buttonR.setText("Right");
form.setWeights(new int[] { 200, 800 });
leftWidth = 200;
form.addListener(SWT.Resize, new Listener() {
#Override
public void handleEvent(Event arg0) {
int width = form.getClientArea().width;
int[] weights = form.getWeights();
double perChange = (double) leftWidth / (double) width;
weights[0] = (int) (perChange * 1000.0);
weights[1] = 1000 - weights[0];
// oldWeights must be set before form.setWeights
oldWeight = weights[0];
form.setWeights(weights);
}
});
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch())
display.sleep();
}
display.dispose();
}
The sash jumps around about 1px when the window is resized due to rounding issues, but it seems to do what I want.
This is a very hacky solution and I would like to know if there is a better one. It also crashes if you make the window smaller than the left button, but that is easy to fix.
Have to rewrite SashForm to get this behavior. new SashForm(Composite parent, int style) is old behavior; new SashForm(Composite parent, int style, false) uses widths/heights specified in pixels.
Not tested with more than two Composite children of the SashForm, and odd behavior if you don't setLength when using new behavior, but will not break existing uses of SashForm
e.g.:
public static void main(String[] args)
{
Display display = new Display();
final Shell shell = new Shell(display);
shell.setText("StackOverflow");
shell.setLayout(new FillLayout());
final SashForm form = new SashForm(shell, SWT.HORIZONTAL);
Button button = new Button(form, SWT.PUSH);
button.setText("Left");
Button buttonR = new Button(form, SWT.PUSH);
buttonR.setText("Right");
form.setLengths(new int[] { 250, 25 });
shell.pack();
shell.setSize(600, 400);
shell.open();
while (!shell.isDisposed())
{
if (!display.readAndDispatch())
display.sleep();
}
display.dispose();
}
First button will default to 250 pixels, second the remainder shell
SashForm.java:
import org.eclipse.swt.*;
import org.eclipse.swt.widgets.*;
import org.eclipse.swt.graphics.*;
public class SashForm extends Composite {
/**
* The width of all sashes in the form.
*/
public int SASH_WIDTH = 3;
int sashStyle;
final private boolean weighted;
Sash[] sashes = new Sash[0];
// Remember background and foreground
// colors to determine whether to set
// sashes to the default color (null) or
// a specific color
Color background = null;
Color foreground = null;
Control[] controls = new Control[0];
Control maxControl = null;
Listener sashListener;
static final int DRAG_MINIMUM = 20;
public SashForm(Composite parent, int style) {
super(parent, checkStyle(style));
super.setLayout(new SashFormLayout());
sashStyle = ((style & SWT.VERTICAL) != 0) ? SWT.HORIZONTAL : SWT.VERTICAL;
if ((style & SWT.BORDER) != 0) sashStyle |= SWT.BORDER;
if ((style & SWT.SMOOTH) != 0) sashStyle |= SWT.SMOOTH;
sashListener = new Listener() {
public void handleEvent(Event e) {
onDragSash(e);
}
};
weighted = true;
}
public SashForm(Composite parent, int style, boolean weighted) {
super(parent, checkStyle(style));
super.setLayout(new SashFormLayout());
sashStyle = ((style & SWT.VERTICAL) != 0) ? SWT.HORIZONTAL : SWT.VERTICAL;
if ((style & SWT.BORDER) != 0) sashStyle |= SWT.BORDER;
if ((style & SWT.SMOOTH) != 0) sashStyle |= SWT.SMOOTH;
sashListener = new Listener() {
public void handleEvent(Event e) {
onDragSash(e);
}
};
this.weighted = weighted;
}
static int checkStyle (int style) {
int mask = SWT.BORDER | SWT.LEFT_TO_RIGHT | SWT.RIGHT_TO_LEFT;
return style & mask;
}
Sash createSash() {
Sash sash = new Sash(this, sashStyle);
sash.setBackground(background);
sash.setForeground(foreground);
sash.setToolTipText(getToolTipText());
sash.addListener(SWT.Selection, sashListener);
return sash;
}
#Override
public int getOrientation() {
//checkWidget();
return (sashStyle & SWT.VERTICAL) != 0 ? SWT.HORIZONTAL : SWT.VERTICAL;
}
/**
* Returns the width of the sashes when the controls in the SashForm are
* laid out.
*
* #return the width of the sashes
*
* #exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*
* #since 3.4
*/
public int getSashWidth() {
checkWidget();
return SASH_WIDTH;
}
#Override
public int getStyle() {
int style = super.getStyle();
style |= getOrientation() == SWT.VERTICAL ? SWT.VERTICAL : SWT.HORIZONTAL;
if ((sashStyle & SWT.SMOOTH) != 0) style |= SWT.SMOOTH;
return style;
}
/**
* Answer the control that currently is maximized in the SashForm.
* This value may be null.
*
* #return the control that currently is maximized or null
*/
public Control getMaximizedControl(){
//checkWidget();
return this.maxControl;
}
public int[] getWeights() {
checkWidget();
Control[] cArray = getControls(false);
int[] ratios = new int[cArray.length];
for (int i = 0; i < cArray.length; i++) {
Object data = cArray[i].getLayoutData();
if (data != null && data instanceof SashFormData) {
ratios[i] = (int)(((SashFormData)data).weight * 1000 >> 16);
} else {
ratios[i] = 200;
}
}
return ratios;
}
Control[] getControls(boolean onlyVisible) {
Control[] children = getChildren();
Control[] result = new Control[0];
for (int i = 0; i < children.length; i++) {
if (children[i] instanceof Sash) continue;
if (onlyVisible && !children[i].getVisible()) continue;
Control[] newResult = new Control[result.length + 1];
System.arraycopy(result, 0, newResult, 0, result.length);
newResult[result.length] = children[i];
result = newResult;
}
return result;
}
boolean getWeighted() {
return weighted;
}
void onDragSash(Event event) {
Sash sash = (Sash)event.widget;
int sashIndex = -1;
for (int i= 0; i < sashes.length; i++) {
if (sashes[i] == sash) {
sashIndex = i;
break;
}
}
if (sashIndex == -1) return;
Control c1 = controls[sashIndex];
Control c2 = controls[sashIndex + 1];
Rectangle b1 = c1.getBounds();
Rectangle b2 = c2.getBounds();
Rectangle sashBounds = sash.getBounds();
Rectangle area = getClientArea();
boolean correction = false;
if (getOrientation() == SWT.HORIZONTAL) {
correction = b1.width < DRAG_MINIMUM || b2.width < DRAG_MINIMUM;
int totalWidth = b2.x + b2.width - b1.x;
int shift = event.x - sashBounds.x;
b1.width += shift;
b2.x += shift;
b2.width -= shift;
if (b1.width < DRAG_MINIMUM) {
b1.width = DRAG_MINIMUM;
b2.x = b1.x + b1.width + sashBounds.width;
b2.width = totalWidth - b2.x;
event.x = b1.x + b1.width;
event.doit = false;
}
if (b2.width < DRAG_MINIMUM) {
b1.width = totalWidth - DRAG_MINIMUM - sashBounds.width;
b2.x = b1.x + b1.width + sashBounds.width;
b2.width = DRAG_MINIMUM;
event.x = b1.x + b1.width;
event.doit = false;
}
Object data1 = c1.getLayoutData();
if (data1 == null || !(data1 instanceof SashFormData)) {
data1 = new SashFormData();
c1.setLayoutData(data1);
}
Object data2 = c2.getLayoutData();
if (data2 == null || !(data2 instanceof SashFormData)) {
data2 = new SashFormData();
c2.setLayoutData(data2);
}
((SashFormData)data1).weight = (((long)b1.width << 16) + area.width - 1) / area.width;
((SashFormData)data1).length = b1.width;
((SashFormData)data2).weight = (((long)b2.width << 16) + area.width - 1) / area.width;
((SashFormData)data2).length = b2.width;
} else {
correction = b1.height < DRAG_MINIMUM || b2.height < DRAG_MINIMUM;
int totalHeight = b2.y + b2.height - b1.y;
int shift = event.y - sashBounds.y;
b1.height += shift;
b2.y += shift;
b2.height -= shift;
if (b1.height < DRAG_MINIMUM) {
b1.height = DRAG_MINIMUM;
b2.y = b1.y + b1.height + sashBounds.height;
b2.height = totalHeight - b2.y;
event.y = b1.y + b1.height;
event.doit = false;
}
if (b2.height < DRAG_MINIMUM) {
b1.height = totalHeight - DRAG_MINIMUM - sashBounds.height;
b2.y = b1.y + b1.height + sashBounds.height;
b2.height = DRAG_MINIMUM;
event.y = b1.y + b1.height;
event.doit = false;
}
Object data1 = c1.getLayoutData();
if (data1 == null || !(data1 instanceof SashFormData)) {
data1 = new SashFormData();
c1.setLayoutData(data1);
}
Object data2 = c2.getLayoutData();
if (data2 == null || !(data2 instanceof SashFormData)) {
data2 = new SashFormData();
c2.setLayoutData(data2);
}
((SashFormData)data1).weight = (((long)b1.height << 16) + area.height - 1) / area.height;
((SashFormData)data2).weight = (((long)b2.height << 16) + area.height - 1) / area.height;
}
if (correction || (event.doit && event.detail != SWT.DRAG)) {
c1.setBounds(b1);
sash.setBounds(event.x, event.y, event.width, event.height);
c2.setBounds(b2);
}
}
#Override
public void setOrientation(int orientation) {
checkWidget();
if (orientation == SWT.RIGHT_TO_LEFT || orientation == SWT.LEFT_TO_RIGHT) {
super.setOrientation(orientation);
return;
}
if (getOrientation() == orientation) return;
if (orientation != SWT.HORIZONTAL && orientation != SWT.VERTICAL) {
SWT.error(SWT.ERROR_INVALID_ARGUMENT);
}
sashStyle &= ~(SWT.HORIZONTAL | SWT.VERTICAL);
sashStyle |= orientation == SWT.VERTICAL ? SWT.HORIZONTAL : SWT.VERTICAL;
for (int i = 0; i < sashes.length; i++) {
sashes[i].dispose();
sashes[i] = createSash();
}
layout(false);
}
#Override
public void setBackground (Color color) {
super.setBackground(color);
background = color;
for (int i = 0; i < sashes.length; i++) {
sashes[i].setBackground(background);
}
}
#Override
public void setForeground (Color color) {
super.setForeground(color);
foreground = color;
for (int i = 0; i < sashes.length; i++) {
sashes[i].setForeground(foreground);
}
}
#Override
public void setLayout (Layout layout) {
checkWidget();
return;
}
public void setMaximizedControl(Control control){
checkWidget();
if (control == null) {
if (maxControl != null) {
this.maxControl = null;
layout(false);
for (int i= 0; i < sashes.length; i++){
sashes[i].setVisible(true);
}
}
return;
}
for (int i= 0; i < sashes.length; i++){
sashes[i].setVisible(false);
}
maxControl = control;
layout(false);
}
public void setSashWidth(int width) {
checkWidget();
if (SASH_WIDTH == width) return;
SASH_WIDTH = width;
layout(false);
}
#Override
public void setToolTipText(String string) {
super.setToolTipText(string);
for (int i = 0; i < sashes.length; i++) {
sashes[i].setToolTipText(string);
}
}
public void setWeights(int[] weights) {
checkWidget();
Control[] cArray = getControls(false);
if (weights == null || weights.length != cArray.length) {
SWT.error(SWT.ERROR_INVALID_ARGUMENT);
}
int total = 0;
for (int i = 0; i < weights.length; i++) {
if (weights[i] < 0) {
SWT.error(SWT.ERROR_INVALID_ARGUMENT);
}
total += weights[i];
}
if (total == 0) {
SWT.error(SWT.ERROR_INVALID_ARGUMENT);
}
for (int i = 0; i < cArray.length; i++) {
Object data = cArray[i].getLayoutData();
if (data == null || !(data instanceof SashFormData)) {
data = new SashFormData();
cArray[i].setLayoutData(data);
}
((SashFormData)data).weight = (((long)weights[i] << 16) + total - 1) / total;
}
layout(false);
}
public void setLengths(int[] lengths) {
checkWidget();
Control[] cArray = getControls(false);
if (lengths == null || lengths.length != cArray.length) {
SWT.error(SWT.ERROR_INVALID_ARGUMENT);
}
int total = 0;
for (int i = 0; i < lengths.length; i++) {
if (lengths[i] < 0) {
SWT.error(SWT.ERROR_INVALID_ARGUMENT);
}
total += lengths[i];
}
if (total == 0) {
SWT.error(SWT.ERROR_INVALID_ARGUMENT);
}
for (int i = 0; i < cArray.length; i++) {
Object data = cArray[i].getLayoutData();
if (data == null || !(data instanceof SashFormData)) {
data = new SashFormData();
cArray[i].setLayoutData(data);
}
((SashFormData)data).length = lengths[i];
}
layout(false);
}
}
SashFormData.java:
class SashFormData {
long weight;
int length;
String getName () {
String string = getClass ().getName ();
int index = string.lastIndexOf ('.');
if (index == -1) return string;
return string.substring (index + 1, string.length ());
}
#Override
public String toString () {
return getName()+" {length="+length+", weight="+weight+"}"; //$NON-NLS-2$
}
}
SashFormLayout.java:
import org.eclipse.swt.*;
import org.eclipse.swt.graphics.*;
import org.eclipse.swt.widgets.*;
class SashFormLayout extends Layout {
#Override
protected Point computeSize(Composite composite, int wHint, int hHint, boolean flushCache) {
SashForm sashForm = (SashForm)composite;
Control[] cArray = sashForm.getControls(true);
int width = 0;
int height = 0;
if (cArray.length == 0) {
if (wHint != SWT.DEFAULT) width = wHint;
if (hHint != SWT.DEFAULT) height = hHint;
return new Point(width, height);
}
// determine control sizes
boolean vertical = sashForm.getOrientation() == SWT.VERTICAL;
if (sashForm.getWeighted()) {
int maxIndex = 0;
int maxValue = 0;
for (int i = 0; i < cArray.length; i++) {
if (vertical) {
Point size = cArray[i].computeSize(wHint, SWT.DEFAULT, flushCache);
if (size.y > maxValue) {
maxIndex = i;
maxValue = size.y;
}
width = Math.max(width, size.x);
} else {
Point size = cArray[i].computeSize(SWT.DEFAULT, hHint, flushCache);
if (size.x > maxValue) {
maxIndex = i;
maxValue = size.x;
}
height = Math.max(height, size.y);
}
}
// get the ratios
long[] ratios = new long[cArray.length];
long total = 0;
for (int i = 0; i < cArray.length; i++) {
Object data = cArray[i].getLayoutData();
if (data != null && data instanceof SashFormData) {
ratios[i] = ((SashFormData)data).weight;
} else {
data = new SashFormData();
cArray[i].setLayoutData(data);
((SashFormData)data).weight = ratios[i] = ((200 << 16) + 999) / 1000;
}
total += ratios[i];
}
if (ratios[maxIndex] > 0) {
int sashwidth = sashForm.sashes.length > 0 ? sashForm.SASH_WIDTH + sashForm.sashes [0].getBorderWidth() * 2 : sashForm.SASH_WIDTH;
if (vertical) {
height += (int)(total * maxValue / ratios[maxIndex]) + (cArray.length - 1) * sashwidth;
} else {
width += (int)(total * maxValue / ratios[maxIndex]) + (cArray.length - 1) * sashwidth;
}
}
} else {
int maxIndex = 0;
int maxValue = 0;
for (int i = 0; i < cArray.length; i++) {
if (vertical) {
Point size = cArray[i].computeSize(wHint, SWT.DEFAULT, flushCache);
if (size.y > maxValue) {
maxIndex = i;
maxValue = size.y;
}
width = Math.max(width, size.x);
} else {
Point size = cArray[i].computeSize(SWT.DEFAULT, hHint, flushCache);
if (size.x > maxValue) {
maxIndex = i;
maxValue = size.x;
}
height = Math.max(height, size.y);
}
}
// get the lengths
int[] lengths = new int[cArray.length];
long total = 0;
for (int i = 0; i < cArray.length; i++) {
Object data = cArray[i].getLayoutData();
if (data != null && data instanceof SashFormData) {
lengths[i] = ((SashFormData)data).length;
} else {
data = new SashFormData();
cArray[i].setLayoutData(data);
((SashFormData)data).length = sashForm.getOrientation() == SWT.HORIZONTAL ? cArray[i].getSize().x : cArray[i].getSize().y;
}
total += lengths[i];
}
if (lengths[maxIndex] > 0) {
int sashwidth = sashForm.sashes.length > 0 ? sashForm.SASH_WIDTH + sashForm.sashes [0].getBorderWidth() * 2 : sashForm.SASH_WIDTH;
if (vertical) {
height += total + (cArray.length - 1) * sashwidth;
} else {
width += total + (cArray.length - 1) * sashwidth;
}
}
}
width += sashForm.getBorderWidth()*2;
height += sashForm.getBorderWidth()*2;
if (wHint != SWT.DEFAULT) width = wHint;
if (hHint != SWT.DEFAULT) height = hHint;
return new Point(width, height);
}
#Override
protected boolean flushCache(Control control) {
return true;
}
#Override
protected void layout(Composite composite, boolean flushCache) {
SashForm sashForm = (SashForm)composite;
Rectangle area = sashForm.getClientArea();
if (area.width <= 1 || area.height <= 1) return;
Control[] newControls = sashForm.getControls(true);
if (sashForm.controls.length == 0 && newControls.length == 0) return;
sashForm.controls = newControls;
Control[] controls = sashForm.controls;
if (sashForm.maxControl != null && !sashForm.maxControl.isDisposed()) {
for (int i= 0; i < controls.length; i++){
if (controls[i] != sashForm.maxControl) {
controls[i].setBounds(-200, -200, 0, 0);
} else {
controls[i].setBounds(area);
}
}
return;
}
// keep just the right number of sashes
if (sashForm.sashes.length < controls.length - 1) {
Sash[] newSashes = new Sash[controls.length - 1];
System.arraycopy(sashForm.sashes, 0, newSashes, 0, sashForm.sashes.length);
for (int i = sashForm.sashes.length; i < newSashes.length; i++) {
newSashes[i] = sashForm.createSash();
}
sashForm.sashes = newSashes;
}
if (sashForm.sashes.length > controls.length - 1) {
if (controls.length == 0) {
for (int i = 0; i < sashForm.sashes.length; i++) {
sashForm.sashes[i].dispose();
}
sashForm.sashes = new Sash[0];
} else {
Sash[] newSashes = new Sash[controls.length - 1];
System.arraycopy(sashForm.sashes, 0, newSashes, 0, newSashes.length);
for (int i = controls.length - 1; i < sashForm.sashes.length; i++) {
sashForm.sashes[i].dispose();
}
sashForm.sashes = newSashes;
}
}
if (controls.length == 0) return;
Sash[] sashes = sashForm.sashes;
if (sashForm.getWeighted()) {
// get the ratios
long[] ratios = new long[controls.length];
long total = 0;
for (int i = 0; i < controls.length; i++) {
Object data = controls[i].getLayoutData();
if (data != null && data instanceof SashFormData) {
ratios[i] = ((SashFormData)data).weight;
} else {
data = new SashFormData();
controls[i].setLayoutData(data);
((SashFormData)data).weight = ratios[i] = ((200 << 16) + 999) / 1000;
}
total += ratios[i];
}
int sashwidth = sashes.length > 0 ? sashForm.SASH_WIDTH + sashes [0].getBorderWidth() * 2 : sashForm.SASH_WIDTH;
if (sashForm.getOrientation() == SWT.HORIZONTAL) {
int width = (int)(ratios[0] * (area.width - sashes.length * sashwidth) / total);
int x = area.x;
controls[0].setBounds(x, area.y, width, area.height);
x += width;
for (int i = 1; i < controls.length - 1; i++) {
sashes[i - 1].setBounds(x, area.y, sashwidth, area.height);
x += sashwidth;
width = (int)(ratios[i] * (area.width - sashes.length * sashwidth) / total);
controls[i].setBounds(x, area.y, width, area.height);
x += width;
}
if (controls.length > 1) {
sashes[sashes.length - 1].setBounds(x, area.y, sashwidth, area.height);
x += sashwidth;
width = area.width - x;
controls[controls.length - 1].setBounds(x, area.y, width, area.height);
}
} else {
int height = (int)(ratios[0] * (area.height - sashes.length * sashwidth) / total);
int y = area.y;
controls[0].setBounds(area.x, y, area.width, height);
y += height;
for (int i = 1; i < controls.length - 1; i++) {
sashes[i - 1].setBounds(area.x, y, area.width, sashwidth);
y += sashwidth;
height = (int)(ratios[i] * (area.height - sashes.length * sashwidth) / total);
controls[i].setBounds(area.x, y, area.width, height);
y += height;
}
if (controls.length > 1) {
sashes[sashes.length - 1].setBounds(area.x, y, area.width, sashwidth);
y += sashwidth;
height = area.height - y;
controls[controls.length - 1].setBounds(area.x, y, area.width, height);
}
}
} else {
// get the lengths
int[] lengths = new int[controls.length];
for (int i = 0; i < controls.length; i++) {
Object data = controls[i].getLayoutData();
if (data != null && data instanceof SashFormData) {
lengths[i] = ((SashFormData)data).length;
} else {
data = new SashFormData();
controls[i].setLayoutData(data);
((SashFormData)data).length = sashForm.getOrientation() == SWT.HORIZONTAL ? controls[i].getSize().x : controls[i].getSize().y;
}
}
int sashwidth = sashes.length > 0 ? sashForm.SASH_WIDTH + sashes [0].getBorderWidth() * 2 : sashForm.SASH_WIDTH;
if (sashForm.getOrientation() == SWT.HORIZONTAL) {
int width = lengths[0];
int x = area.x;
controls[0].setBounds(x, area.y, width, area.height);
x += width;
for (int i = 1; i < controls.length - 1; i++) {
sashes[i - 1].setBounds(x, area.y, sashwidth, area.height);
x += sashwidth;
width = lengths[i];
controls[i].setBounds(x, area.y, width, area.height);
x += width;
}
if (controls.length > 1) {
sashes[sashes.length - 1].setBounds(x, area.y, sashwidth, area.height);
x += sashwidth;
width = area.width - x;
controls[controls.length - 1].setBounds(x, area.y, width, area.height);
}
} else {
int height = lengths[0];
int y = area.y;
controls[0].setBounds(area.x, y, area.width, height);
y += height;
for (int i = 1; i < controls.length - 1; i++) {
sashes[i - 1].setBounds(area.x, y, area.width, sashwidth);
y += sashwidth;
height = lengths[i];
controls[i].setBounds(area.x, y, area.width, height);
y += height;
}
if (controls.length > 1) {
sashes[sashes.length - 1].setBounds(area.x, y, area.width, sashwidth);
y += sashwidth;
height = area.height - y;
controls[controls.length - 1].setBounds(area.x, y, area.width, height);
}
}
}
}
}

Categories