I'm trying to update just a portion of a canvas in SWT, but I don't understand how to do it.
I read tht I have to use the setClipping, the documentation indeed says:
"Sets the area of the receiver which can be changed by drawing operations to the rectangular area specified by the argument. Specifying null for the rectangle reverts the receiver's clipping area to its original value."
So I have just tried but with no luck, here a simple example:
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.*;
import org.eclipse.swt.graphics.*;
import org.eclipse.swt.layout.*;
import org.eclipse.swt.widgets.*;
public class SimpleCanvas {
boolean manualDraw=false;
public void run() {
Display display = new Display();
Shell shell = new Shell(display);
shell.setText("Canvas Example");
createContents(shell);
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
display.dispose();
}
/**
* Creates the main window's contents
*
* #param shell the main window
*/
private void createContents(Shell shell) {
shell.setLayout(new FillLayout());
// Create a canvas
Canvas canvas = new Canvas(shell, SWT.NONE);
// Create a button on the canvas
Button button = new Button(shell, SWT.PUSH);
button.setBounds(10, 10, 300, 40);
button.setText("TEST");
button.addListener(SWT.Selection, new Listener() {
public void handleEvent(Event e) {
switch (e.type) {
case SWT.Selection:
manualDraw=true;
canvas.redraw();
break;
}
}
});
// Create a paint handler for the canvas
canvas.addPaintListener(new PaintListener() {
public void paintControl(PaintEvent e) {
if (manualDraw){
e.gc.setForeground(e.display.getSystemColor(SWT.COLOR_GREEN));
e.gc.setClipping(90,90,60,60);
e.gc.drawRectangle(90,90,30,30);
return ;
}
Rectangle rect = ((Canvas) e.widget).getBounds();
e.gc.setForeground(e.display.getSystemColor(SWT.COLOR_RED));
e.gc.drawText("DRAW TEXT", 0, 0);
e.gc.dispose();
}
});
}
/**
* The application entry point
*
* #param args the command line arguments
*/
public static void main(String[] args) {
new SimpleCanvas().run();
}
}
Can you please help me to understand what I'm doing wrong?
Thank you in advance.
I found the problem. In order to update only a portion of the canvas I don't have to call :
canvas.redraw();
and drawing there a portion of the canvas, but instead get the GC from canvas and use the setClipping there, so invoke something like that:
public void redrawCanvas (Canvas canvas) {
GC gc = new GC(canvas);
gc.setClipping(90,90,60,60);
gc.drawRectangle(90,90,30,30);
gc.dispose();
}
Related
I'm searching for a way to add an overlay over some composites in my application. The overlay will contain an label with text "No data available". The underlying composite need to be shown but the user cannot do anything. My application contains different composite part in one screen so I need a way to only place the overlay over one of the composites. Is there a way to implement this in SWT?
A possible solution would be to put a semi-transparent Shell with no trimmings over the Composite you want to cover.
The tricky part is to update the overlay Shell to continuously match the size, position and visibility of the Composite and its parents (since they also could affect the children bounds and visibility).
So I decided to try to make a class Overlay to do that; it can be used to cover any Control and it uses control and paint listeners to track and match the underlying Control. These listeners are also attached to the whole hierarchy of parents of the Control.
You can set the color, the transparency and a text over the Overlay using the corresponding methods.
I made some simple tests and it seemed to work correctly, but I can't guarantee anything. You might want to give it a try it.
A simple example using it:
public class OverlayTest {
public static void main(String[] args) {
Display display = new Display();
Shell shell = new Shell(display);
shell.setLayout(new FillLayout(SWT.VERTICAL));
shell.setSize(250, 250);
// create the composite
Composite composite = new Composite(shell, SWT.NONE);
composite.setLayout(new FillLayout(SWT.VERTICAL));
// add stuff to the composite
for (int i = 0; i < 5; i++) {
new Text(composite, SWT.BORDER).setText("Text " + i);
}
// create the overlay over the composite
Overlay overlay = new Overlay(composite);
overlay.setText("No data available");
// create the button to show/hide the overlay
Button button = new Button(shell, SWT.PUSH);
button.setText("Show/hide overlay");
button.addSelectionListener(new SelectionAdapter() {
#Override
public void widgetSelected(SelectionEvent arg0) {
// if the overlay is showing we hide it, otherwise we show it
if (overlay.isShowing()) {
overlay.remove();
}
else {
overlay.show();
}
}
});
shell.open();
while (shell != null && !shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
}
}
And the Overlay class:
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.events.ControlListener;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Scrollable;
import org.eclipse.swt.widgets.Shell;
/**
* A customizable overlay over a control.
*
* #author Loris Securo
*/
public class Overlay {
private List<Composite> parents;
private Control objectToOverlay;
private Shell overlay;
private Label label;
private ControlListener controlListener;
private DisposeListener disposeListener;
private PaintListener paintListener;
private boolean showing;
private boolean hasClientArea;
private Scrollable scrollableToOverlay;
public Overlay(Control objectToOverlay) {
Objects.requireNonNull(objectToOverlay);
this.objectToOverlay = objectToOverlay;
// if the object to overlay is an instance of Scrollable (e.g. Shell) then it has
// the getClientArea method, which is preferable over Control.getSize
if (objectToOverlay instanceof Scrollable) {
hasClientArea = true;
scrollableToOverlay = (Scrollable) objectToOverlay;
}
else {
hasClientArea = false;
scrollableToOverlay = null;
}
// save the parents of the object, so we can add/remove listeners to them
parents = new ArrayList<Composite>();
Composite parent = objectToOverlay.getParent();
while (parent != null) {
parents.add(parent);
parent = parent.getParent();
}
// listener to track position and size changes in order to modify the overlay bounds as well
controlListener = new ControlListener() {
#Override
public void controlMoved(ControlEvent e) {
reposition();
}
#Override
public void controlResized(ControlEvent e) {
reposition();
}
};
// listener to track paint changes, like when the object or its parents become not visible (for example changing tab in a TabFolder)
paintListener = new PaintListener() {
#Override
public void paintControl(PaintEvent arg0) {
reposition();
}
};
// listener to remove the overlay if the object to overlay is disposed
disposeListener = new DisposeListener() {
#Override
public void widgetDisposed(DisposeEvent e) {
remove();
}
};
// create the overlay shell
overlay = new Shell(objectToOverlay.getShell(), SWT.NO_TRIM);
// default values of the overlay
overlay.setBackground(objectToOverlay.getDisplay().getSystemColor(SWT.COLOR_GRAY));
overlay.setAlpha(200);
// so the label can inherit the background of the overlay
overlay.setBackgroundMode(SWT.INHERIT_DEFAULT);
// label to display a text
// style WRAP so if it is too long the text get wrapped
label = new Label(overlay, SWT.WRAP);
// to center the label
overlay.setLayout(new GridLayout());
label.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true, true));
showing = false;
overlay.open();
overlay.setVisible(showing);
}
public void show() {
// if it's already visible we just exit
if (showing) {
return;
}
// set the overlay position over the object
reposition();
// show the overlay
overlay.setVisible(true);
// add listeners to the object to overlay
objectToOverlay.addControlListener(controlListener);
objectToOverlay.addDisposeListener(disposeListener);
objectToOverlay.addPaintListener(paintListener);
// add listeners also to the parents because if they change then also the visibility of our object could change
for (Composite parent : parents) {
parent.addControlListener(controlListener);
parent.addPaintListener(paintListener);
}
showing = true;
}
public void remove() {
// if it's already not visible we just exit
if (!showing) {
return;
}
// remove the listeners
if (!objectToOverlay.isDisposed()) {
objectToOverlay.removeControlListener(controlListener);
objectToOverlay.removeDisposeListener(disposeListener);
objectToOverlay.removePaintListener(paintListener);
}
// remove the parents listeners
for (Composite parent : parents) {
if (!parent.isDisposed()) {
parent.removeControlListener(controlListener);
parent.removePaintListener(paintListener);
}
}
// remove the overlay shell
if (!overlay.isDisposed()) {
overlay.setVisible(false);
}
showing = false;
}
public void setBackground(Color background) {
overlay.setBackground(background);
}
public Color getBackground() {
return overlay.getBackground();
}
public void setAlpha(int alpha) {
overlay.setAlpha(alpha);
}
public int getAlpha() {
return overlay.getAlpha();
}
public boolean isShowing() {
return showing;
}
public void setText(String text) {
label.setText(text);
// to adjust the label size accordingly
overlay.layout();
}
public String getText() {
return label.getText();
}
private void reposition() {
// if the object is not visible, we hide the overlay and exit
if (!objectToOverlay.isVisible()) {
overlay.setBounds(new Rectangle(0, 0, 0, 0));
return;
}
// if the object is visible we need to find the visible region in order to correctly place the overlay
// get the display bounds of the object to overlay
Point objectToOverlayDisplayLocation = objectToOverlay.toDisplay(0, 0);
Point objectToOverlaySize;
// if it has a client area, we prefer that instead of the size
if (hasClientArea) {
Rectangle clientArea = scrollableToOverlay.getClientArea();
objectToOverlaySize = new Point(clientArea.width, clientArea.height);
}
else {
objectToOverlaySize = objectToOverlay.getSize();
}
Rectangle objectToOverlayBounds = new Rectangle(objectToOverlayDisplayLocation.x, objectToOverlayDisplayLocation.y, objectToOverlaySize.x,
objectToOverlaySize.y);
Rectangle intersection = objectToOverlayBounds;
// intersect the bounds of the object with its parents bounds so we get only the visible bounds
for (Composite parent : parents) {
Rectangle parentClientArea = parent.getClientArea();
Point parentLocation = parent.toDisplay(parentClientArea.x, parentClientArea.y);
Rectangle parentBounds = new Rectangle(parentLocation.x, parentLocation.y, parentClientArea.width, parentClientArea.height);
intersection = intersection.intersection(parentBounds);
// if intersection has no size then it would be a waste of time to continue
if (intersection.width == 0 || intersection.height == 0) {
break;
}
}
overlay.setBounds(intersection);
}
}
Is there a way in Java to give a label a highlight effect?
For example, say I have a label with a picture set to it, and I want to highlight it whenever the mouse cursor enters it.
I tried to look around in google and the only tip I could find is to use label.setBackground(display.getSystemColor(SWT.COLOR_YELLOW));
but this obviously will not highlight the label, let alone a label with a picture on it.
because first of all, it is too yellow, and second, it will appear as if the color is behind the picture (well, because it is setBackground() of course).
I'm looking for a much brighter yellow color that will also be transparent, so the image will be seen.
Hope my intentions are clear enough.
You could add a Listener to SWT.MouseEnter and SWT.MouseExit and keep track of whether the mouse is hovering over the Label and then repaint it with a custom Listener for SWT.Paint:
private static boolean hovering = false;
public static void main(String[] args)
{
final Display display = new Display();
Shell shell = new Shell(display);
shell.setText("StackOverflow");
shell.setLayout(new FillLayout());
final Label label = new Label(shell, SWT.NONE);
final Image image = display.getSystemImage(SWT.ICON_ERROR);
Listener mouseHover = new Listener()
{
#Override
public void handleEvent(Event e)
{
hovering = e.type == SWT.MouseEnter;
label.redraw();
}
};
label.addListener(SWT.MouseEnter, mouseHover);
label.addListener(SWT.MouseExit, mouseHover);
label.addListener(SWT.Paint, new Listener()
{
#Override
public void handleEvent(Event e)
{
int x = label.getBounds().width / 2 - image.getBounds().width / 2;
int y = label.getBounds().height / 2 - image.getBounds().height / 2;
e.gc.drawImage(image, x, y);
if(hovering)
{
e.gc.setAlpha(50);
e.gc.setBackground(display.getSystemColor(SWT.COLOR_YELLOW));
e.gc.fillRectangle(label.getBounds());
}
}
});
shell.pack();
shell.setSize(100, 100);
shell.open();
while (!shell.isDisposed())
{
if (!display.readAndDispatch())
display.sleep();
}
display.dispose();
}
Looks like this:
i am showing simple code sample. I showed an gif image in a Jlabel. When run the programme, TASK manager shows that memory is increasing continuously. Why it happens?
Edited:
try this code please... on show glass button, glass panel is shown with gif image and a hide glass button in it and with that memory will be started increasing. On clicking hide glass button, glass panel will be hidden and memory increasing will b stopped.
#mKorbel : I had debugged it, the constructor will be called once, so no re-initializing of JFrame and also included : setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
public class circle extends JFrame {
public ImageIcon pic;
final JPanel glass;
public JButton glass_show;
public JButton hide_glass;
public circle() {
super("Hi shamansdsdsd");
setSize(500, 300);
// Image icon initialize once :
pic = new ImageIcon("images/loadinag.gif");
glass_show = new JButton("Show Glass panel");
this.add(glass_show);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
glass = (JPanel) this.getGlassPane();
hide_glass = new JButton("Hide Glass panel");
glass.add(hide_glass);
glass.add(new JLabel(pic));
glass.setOpaque(false);
}
public void initialize_listeners(){
glass_show.addActionListener(new ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent A) {
glass.setVisible(true);
}
});
hide_glass.addActionListener(new ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent A) {
glass.setVisible(false);
}
});
}
public static void main(String[] args) {
circle mFrame = new circle();
mFrame.initialize_listeners();
mFrame.setVisible(true);
}
}
There is a bug in Java with animated GIF images. There is no memory increase with other images.
Edit;
Below example runs without memory leak; but you need Eclipse SWT library from Eclipse's site
import org.eclipse.swt.SWT;
import org.eclipse.swt.SWTException;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.ImageData;
import org.eclipse.swt.graphics.ImageLoader;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.FileDialog;
import org.eclipse.swt.widgets.Shell;
public class GIFExample {
static Display display;
static Shell shell;
static GC shellGC;
static Color shellBackground;
static ImageLoader loader;
static ImageData[] imageDataArray;
static Thread animateThread;
static Image image;
static final boolean useGIFBackground = false;
public static void main(String[] args) {
display = new Display();
shell = new Shell(display);
shell.setSize(300, 300);
shell.open();
shellGC = new GC(shell);
shellBackground = shell.getBackground();
FileDialog dialog = new FileDialog(shell);
dialog.setFilterExtensions(new String[] {"*.gif"});
String fileName = dialog.open();
if (fileName != null) {
loader = new ImageLoader();
try {
imageDataArray = loader.load(fileName);
if (imageDataArray.length > 1) {
animateThread = new Thread("Animation") {
#Override
public void run() {
/* Create an off-screen image to draw on, and fill it with the shell background. */
Image offScreenImage = new Image(display, loader.logicalScreenWidth, loader.logicalScreenHeight);
GC offScreenImageGC = new GC(offScreenImage);
offScreenImageGC.setBackground(shellBackground);
offScreenImageGC.fillRectangle(0, 0, loader.logicalScreenWidth, loader.logicalScreenHeight);
try {
/* Create the first image and draw it on the off-screen image. */
int imageDataIndex = 0;
ImageData imageData = imageDataArray[imageDataIndex];
if (image != null && !image.isDisposed()) image.dispose();
image = new Image(display, imageData);
offScreenImageGC.drawImage(
image,
0,
0,
imageData.width,
imageData.height,
imageData.x,
imageData.y,
imageData.width,
imageData.height);
/* Now loop through the images, creating and drawing each one
* on the off-screen image before drawing it on the shell. */
int repeatCount = loader.repeatCount;
while (loader.repeatCount == 0 || repeatCount > 0) {
switch (imageData.disposalMethod) {
case SWT.DM_FILL_BACKGROUND:
/* Fill with the background color before drawing. */
Color bgColor = null;
if (useGIFBackground && loader.backgroundPixel != -1) {
bgColor = new Color(display, imageData.palette.getRGB(loader.backgroundPixel));
}
offScreenImageGC.setBackground(bgColor != null ? bgColor : shellBackground);
offScreenImageGC.fillRectangle(imageData.x, imageData.y, imageData.width, imageData.height);
if (bgColor != null) bgColor.dispose();
break;
case SWT.DM_FILL_PREVIOUS:
/* Restore the previous image before drawing. */
offScreenImageGC.drawImage(
image,
0,
0,
imageData.width,
imageData.height,
imageData.x,
imageData.y,
imageData.width,
imageData.height);
break;
}
imageDataIndex = (imageDataIndex + 1) % imageDataArray.length;
imageData = imageDataArray[imageDataIndex];
image.dispose();
image = new Image(display, imageData);
offScreenImageGC.drawImage(
image,
0,
0,
imageData.width,
imageData.height,
imageData.x,
imageData.y,
imageData.width,
imageData.height);
/* Draw the off-screen image to the shell. */
shellGC.drawImage(offScreenImage, 0, 0);
/* Sleep for the specified delay time (adding commonly-used slow-down fudge factors). */
try {
int ms = imageData.delayTime * 10;
if (ms < 20) ms += 30;
if (ms < 30) ms += 10;
Thread.sleep(ms);
} catch (InterruptedException e) {
}
/* If we have just drawn the last image, decrement the repeat count and start again. */
if (imageDataIndex == imageDataArray.length - 1) repeatCount--;
}
} catch (SWTException ex) {
System.out.println("There was an error animating the GIF");
} finally {
if (offScreenImage != null && !offScreenImage.isDisposed()) offScreenImage.dispose();
if (offScreenImageGC != null && !offScreenImageGC.isDisposed()) offScreenImageGC.dispose();
if (image != null && !image.isDisposed()) image.dispose();
}
}
};
animateThread.setDaemon(true);
animateThread.start();
}
} catch (SWTException ex) {
System.out.println("There was an error loading the GIF");
}
}
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) display.sleep();
}
display.dispose();
}
}
Code Source
there are two areas
1) you forgot to declare setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); then current JVM instance is still in (the Native OS) RAM, until PC restart or power-off
2) maybe you are create a new JFrame for every Images on the fly
I create a GC on the display, and then I do some drawing. My question is how do I un-draw?
The code looks like this:
final GC gc = new GC(display);
gc.setForeground(display.getSystemColor(SWT.COLOR_RED));
gc.setLineWidth(5);
gc.drawRectangle(rectangle);
gc.dispose();
Context:
I need to let users select a window from other applications. The behavior I expect can be seen here: http://tools.tortoisesvn.net/SendMessage.html Instead, All my screen is filled with red rectangles.
It is OK for me even if it is a Windows-only solution.
EDIT: sorry, red garbage remains even after I close my application.
EDIT2: The working example:
public static void main(String[] args) {
final Display display = new Display();
final Shell shell = new Shell(display);
shell.addListener(SWT.MouseMove, new Listener() {
#Override
public void handleEvent(Event event) {
final Point displayPoint = display.map(shell, null, event.x, event.y);
final POINT point = new POINT();
point.x = displayPoint.x;
point.y = displayPoint.y;
final int windowHandle = OS.WindowFromPoint(point);
if (windowHandle != 0 && windowHandle != shell.handle) {
RECT rect = new RECT();
if (OS.GetWindowRect(windowHandle, rect)) {
Rectangle rectangle = new Rectangle(rect.left, rect.top, rect.right - rect.left,
rect.bottom - rect.top);
final GC gc = new GC(display);
gc.setForeground(display.getSystemColor(SWT.COLOR_RED));
gc.setLineWidth(5);
gc.drawRectangle(rectangle);
gc.dispose();
}
}
}
});
shell.pack();
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
display.dispose();
}
To use it, start a mouse drag from the shell (not the title bar) and hover it over an application that uses real windows controls (not swing, QT, XUL). A good example of target application is Total Commander. You will see that the screen becomes full of red rectangles. Ideally I would like to have only one red rectangle visible.
I know I could make a new shell with regions that will simulate the red rectangle, but if the mouse jumps over that, I'm stuck.
I make some code. It's not perfect solution, cause after many tries I'm not able to make transparency everything except the "window" border, so I'm just making the whole shell (which covers the "window" area) partially transparent (and it makes nice effect though).
Here's the code
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.internal.win32.OS;
import org.eclipse.swt.internal.win32.POINT;
import org.eclipse.swt.internal.win32.RECT;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Canvas;
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 ShellBorder {
private Display display = new Display();
private Shell shell = new Shell(display);
private RECT currRect = null;
private Shell paintShell = null;
public ShellBorder() {
shell.addListener(SWT.MouseUp, new Listener() {
#Override
public void handleEvent(Event event) {
paintShell.dispose();
// do whatever you need
// ...
currRect = null;
}
});
shell.addListener(SWT.MouseMove, new Listener() {
#Override
public void handleEvent(Event event) {
final Point displayPoint = display.map(shell, null, event.x, event.y);
final POINT point = new POINT();
point.x = displayPoint.x;
point.y = displayPoint.y;
if(currRect == null) {
getWindowAndDrawBorder(point);
} else {
// cursor is outside the current rectangle
if (point.x < currRect.left || point.x > currRect.right || point.y < currRect.top || point.y > currRect.bottom) {
currRect = null;
paintShell.dispose();
getWindowAndDrawBorder(point);
}
}
}
private void getWindowAndDrawBorder(POINT point) {
long windowHandle = OS.WindowFromPoint(point);
if (windowHandle != 0 && windowHandle != shell.handle) {
RECT rect = new RECT();
if (OS.GetWindowRect(windowHandle, rect)) {
currRect = rect;
paintShell = new Shell(display, SWT.NO_TRIM | SWT.ON_TOP);
paintShell.setLocation(currRect.left, currRect.top);
paintShell.setSize(currRect.right - currRect.left, currRect.bottom - currRect.top);
paintShell.setLayout(new FillLayout());
paintShell.setAlpha(50);
Canvas canvas = new Canvas(paintShell, SWT.NO_BACKGROUND);
canvas.addPaintListener(new PaintListener() {
public void paintControl(PaintEvent e) {
GC gc = e.gc;
gc.setForeground(display.getSystemColor(SWT.COLOR_RED));
gc.setLineWidth(5);
gc.drawRectangle(new Rectangle(0, 0, paintShell.getSize().x, paintShell.getSize().y));
}
});
paintShell.open();
}
}
}
});
shell.pack();
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
display.dispose();
}
public static void main(String[] args) {
new ShellBorder();
}
}
To do this, you must draw on a Shell that covers the complete display. When the Shell is disposed, the drawn rectangles are removed.
I don't know how to find the window under the cursor though...
In my first answer I assumed that you wanted to test for a window of other applications on the Desktop. As that is not the case, you should have a look at my answer to How to draw over child elements of a Composite in SWT? which automatically handles redraw of the relevant parts when a rectangle should be removed again...
In a Java SWT shell window, how do I set its inner size than its whole window frame size?
For instance, if I use shell.setSize(300, 250) this would make the whole window appearing as exactly 300x250. This 300x250 includes the size of the window frame.
How can I set the inner size, that is the content display region of the shell window to 300x250 instead? That's this 300x250 excludes the width of the window frame.
I tried to minus some offset values but the thing is different Operating Systems have different window frame sizes. So having a constant offset would not be accurate.
Thanks.
From your question what I understood is that you want to set the dimension of the Client Area. And in SWT lingo it is defined as a rectangle which describes the area of the receiver which is capable of displaying data (that is, not covered by the "trimmings").
You cannot directly set the dimension of Client Area because there is no API for it. Although you can achieve this by a little hack. In the below sample code I want my client area to be 300 by 250. To achieve this I have used the shell.addShellListener() event listener. When the shell is completely active (see the public void shellActivated(ShellEvent e)) then I calculate the different margins and again set the size of my shell. The calculation and resetting of the shell size gives me the desired shell size.
>>Code:
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ShellEvent;
import org.eclipse.swt.events.ShellListener;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.Shell;
public class MenuTest {
public static void main (String [] args)
{
Display display = new Display ();
final Shell shell = new Shell (display);
GridLayout layout = new GridLayout();
layout.marginHeight = 0;
layout.marginWidth = 0;
layout.horizontalSpacing = 0;
layout.verticalSpacing = 0;
layout.numColumns = 1;
shell.setLayout(layout);
shell.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true,true));
final Menu bar = new Menu (shell, SWT.BAR);
shell.setMenuBar (bar);
shell.addShellListener(new ShellListener() {
public void shellIconified(ShellEvent e) {
}
public void shellDeiconified(ShellEvent e) {
}
public void shellDeactivated(ShellEvent e) {
}
public void shellClosed(ShellEvent e) {
System.out.println("Client Area: " + shell.getClientArea());
}
public void shellActivated(ShellEvent e) {
int frameX = shell.getSize().x - shell.getClientArea().width;
int frameY = shell.getSize().y - shell.getClientArea().height;
shell.setSize(300 + frameX, 250 + frameY);
}
});
shell.open ();
while (!shell.isDisposed()) {
if (!display.readAndDispatch ()) display.sleep ();
}
display.dispose ();
}
}
If I get you right you should set the size of the inner component to the needed size and use the method pack() (of the frame).
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.*;
import org.eclipse.swt.widgets.*;
public class SWTClientAreaTest
{
Display display;
Shell shell;
final int DESIRED_CLIENT_AREA_WIDTH = 300;
final int DESIRED_CLIENT_AREA_HEIGHT = 200;
void render()
{
display = Display.getDefault();
shell = new Shell(display, SWT.SHELL_TRIM | SWT.CENTER);
Point shell_size = shell.getSize();
Rectangle client_area = shell.getClientArea();
shell.setSize
(
DESIRED_CLIENT_AREA_WIDTH + shell_size.x - client_area.width,
DESIRED_CLIENT_AREA_HEIGHT + shell_size.y - client_area.height
);
shell.open();
while (!shell.isDisposed())
{
if (!display.readAndDispatch())
{
display.sleep();
}
}
display.dispose();
}
public static void main(String[] args)
{
SWTClientAreaTest appl = new SWTClientAreaTest();
appl.render();
}
}
Use computeTrim to calculate the bounds that are necessary to display a given client area. The method returns a rectangle that describes the bounds that are needed to provide room for the client area specified in the arguments.
In this example the size of the shell is set so that it is capable to display a client area of 100 x 200 (width x height):
Rectangle bounds = shell.computeTrim(0, 0, 100, 200);
shell.setSize(bounds.width, bounds.height);
This article describes the terms used by SWT for widget dimensions:
https://www.eclipse.org/articles/Article-Understanding-Layouts/Understanding-Layouts.htm