How can I detect if a user has a retina display in Java? I am already aware of detecting the scale factor using Toolkit.getDefaultToolkit().getDesktopProperty("apple.awt.contentScaleFactor"), but java won't let me convert the returned value into an int. I'm wondering how I can convert that into an int, or another way to detect retina displays.
I would get the value this way -
public static boolean hasRetinaDisplay() {
Object obj = Toolkit.getDefaultToolkit()
.getDesktopProperty(
"apple.awt.contentScaleFactor");
if (obj instanceof Float) {
Float f = (Float) obj;
int scale = f.intValue();
return (scale == 2); // 1 indicates a regular mac display.
}
return false;
}
Beware that users may have multiple displays! What does “detect a Retina display” mean in this scenario?
For most purposes, you are interested in rendering an image onto a GUI component. You therefore need to detect what display the component is on.
Luckily java.awt.Component has a getGraphicsConfiguration method that gives us the necessary information.
However, Java 8 (and 7) and Java 9 require different handling: Java 9 exposes the necessary information directly via the graphics device’s default transform. Java 7 and 8 also expose this transformation, but it is always set to an identity transformation (i.e. no transformation), even for a Retina display.
For Java < 9, we need to use reflection to query macOS specific fields in the OpenJDK classes that implement graphics for Mac.
The following class implements the necessary checks for Retina displays and works for Java 8 as well as Java 9. Java 7 might also work with trivial changes but I didn’t test it.
package me.klmr.ui;
import java.awt.*;
import java.lang.reflect.Method;
public final class Device {
private enum JavaVersion {
V8,
V9
}
private static final JavaVersion JAVA_VERSION = getJavaVersion();
private static JavaVersion getJavaVersion() {
final String versionString = System.getProperty("java.version");
if (versionString.startsWith("1.8")) return JavaVersion.V8;
if (versionString.startsWith("9.")) return JavaVersion.V9;
throw new RuntimeException("Unsupported Java version");
}
public static GraphicsConfiguration getCurrentConfiguration(final Component component) {
final GraphicsConfiguration graphicsConfiguration = component.getGraphicsConfiguration();
if (graphicsConfiguration == null) {
return GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
} else {
return graphicsConfiguration;
}
}
public static GraphicsDevice getCurrentDevice(final Component component) {
final GraphicsConfiguration graphicsConfiguration = component.getGraphicsConfiguration();
if (graphicsConfiguration == null) {
return GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice();
} else {
return graphicsConfiguration.getDevice();
}
}
public static boolean isOnRetinaDisplay(final Component component) {
switch (JAVA_VERSION) {
case V8: return isOnRetinaDisplayJava8(component);
case V9: return isOnRetinaDisplayJava9(component);
default: throw new AssertionError("Unreachable");
}
}
public static double getDisplayScalingFactor(final Component component) {
switch (JAVA_VERSION) {
case V8: return getDisplayScalingFactorJava8(component);
case V9: return getDisplayScalingFactorJava9(component);
default: throw new AssertionError("Unreachable");
}
}
private static boolean isOnRetinaDisplayJava8(final Component component) {
final GraphicsDevice device = getCurrentDevice(component);
try {
final Method getScaleFactorMethod = device.getClass().getMethod("getScaleFactor");
final Object scale = getScaleFactorMethod.invoke(device);
return scale instanceof Integer && ((Integer) scale).intValue() == 2;
} catch (ReflectiveOperationException e) {
return false;
}
}
private static boolean isOnRetinaDisplayJava9(final Component component) {
return ! getCurrentConfiguration(component).getDefaultTransform().isIdentity();
}
private static double getDisplayScalingFactorJava8(final Component component) {
return isOnRetinaDisplayJava8(component) ? 2.0 : 1.0;
}
private static double getDisplayScalingFactorJava9(final Component component) {
return getCurrentConfiguration(component).getDefaultTransform().getScaleX();
}
}
In practice, moving a dialog from one screen to another will cause components to re-render. If the component’s rendering code uses the above class to find out the correct resolution, they will render correctly regardless of which display they are currently on.
For Java 9, this also works:
public static boolean isMacRetinaDisplay() {
final GraphicsConfiguration gfxConfig = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
final AffineTransform transform = gfxConfig.getDefaultTransform();
return !transform.isIdentity();
}
You could alternatively inspect the scale factor of the transform and check if it is equal to 2 and fall back to non-retina otherwise.
Related
I am programming an overlay in Java showing information for multiple windows.
I need it to follow the window that it is tracking, and to do this I regularly take information about the current windows to update the position of my overlay. But I would also need to know if the windows are visible to hide the overlay if not. Ideally I should be able to do all this in real time but I'm afraid it's too performance-intensive.
I do all of this with JNA
public interface User32 extends StdCallLibrary {
User32 INSTANCE = (User32) Native.load("user32", User32.class, W32APIOptions.DEFAULT_OPTIONS);
HWND FindWindow(String lpClassName, String lpWindowName);
int GetWindowRect(HWND handle, int[] rect);
boolean IsWindowVisible(HWND handle); // always true if window exist
}
public static int[] getWindowInformation(String windowName) {
int[] rectangle = {0,0,0,0};
HWND hwnd = User32.INSTANCE.FindWindow(null, windowName);
User32.INSTANCE.GetWindowRect(hwnd, rectangle);
boolean res = User32.INSTANCE.IsWindowVisible(hwnd);
System.out.println(windowName + " is visible ? " + res);
return rectangle;
}
Here is my code, you can see that I tried "IsWindowVisible" after read entirely the User32 API of JNA, but it doesn't do what I want.
Your intuition to use IsWindowVisible from User32 is good. JNA has actually implemented this in its WindowUtils class.
List<DesktopWindow> windows = WindowUtils.getAllWindows(true);
The boolean parameter is onlyVisibleWindows.
Note that "visible" here refers to the state of the window itself, not whether it is minimized (may have off-screen or "zero" coordinate) or "behind" vs. "on top" of other windows and thus visible to the user. From the IsWindowVisible documentation (note the second paragraph):
If the specified window, its parent window, its parent's parent window, and so forth, have the WS_VISIBLE style, the return value is nonzero. Otherwise, the return value is zero.
Because the return value specifies whether the window has the WS_VISIBLE style, it may be nonzero even if the window is totally obscured by other windows.
In order to determine whether the window is partially or fully obscured, you would have to evaluate its locAndSize rectangle relative to all windows "in front" of it using Z-order. You could do this on a pixel-by-pixel basis or just evaluate the corner points, depending on how you want to handle partially obscured windows.
The JNA getAllWindows() method returns a list (filtered by visibility) of JNA DesktopWindows encapsulating these fields:
private HWND hwnd;
private String title;
private String filePath;
private Rectangle locAndSize;
Internally you can see the implementation in the inner class W32WindowUtils, which uses a callback function sent to User32's EnumWindows function:
#Override
public List<DesktopWindow> getAllWindows(final boolean onlyVisibleWindows) {
final List<DesktopWindow> result = new LinkedList<DesktopWindow>();
final WNDENUMPROC lpEnumFunc = new WNDENUMPROC() {
#Override
public boolean callback(final HWND hwnd, final Pointer arg1) {
try {
final boolean visible = !onlyVisibleWindows
|| User32.INSTANCE.IsWindowVisible(hwnd);
if (visible) {
final String title = getWindowTitle(hwnd);
final String filePath = getProcessFilePath(hwnd);
final Rectangle locAndSize = getWindowLocationAndSize(hwnd);
result.add(new DesktopWindow(hwnd, title, filePath,
locAndSize));
}
} catch (final Exception e) {
// FIXME properly handle whatever error is raised
e.printStackTrace();
}
return true;
}
};
if (!User32.INSTANCE.EnumWindows(lpEnumFunc, null))
throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
return result;
}
I maintain the JNA-based OSHI project, and have implemented this cross-platform via new SystemInfo().getOperatingSystem().getDesktopWindows(), which uses the above implementation on Windows and adds information from other functions to get the window ordering. OSHI's return list includes these fields and the returned list is already sorted in order to aid you in determining user visibility:
private final long windowId;
private final String title;
private final String command;
private final Rectangle locAndSize;
private final long owningProcessId;
private final int order;
private final boolean visible;
I'm attempting to use the Chinese (Traditional, Taiwan), Chinese (Traditional) - New Phonetic keyboard on an English (US) Windows 7. When I type into a Java Swing-based text area, the candidate list is showing up on the bottom-right of my screen, regardless of where the text area is positioned on the screen. When I'm not using a Java program, the candidate list shows up in the correct place, directly under the text I'm typing.
Has anybody else run into this behavior and found a workaround for it? I haven't found other reports of this behavior online.
Thanks in advance for any help!
System Details:
Microsoft New Phonetic IME 10.1 (10.1.7601.0)
Chinese input mode
Either half or full shape (doesn't matter)
Standard keyboard layout
Windows 7, 64-bit (same happens on 32-bit)
Affects Java 6, 7, and 8
Affects Swing and JavaFX
I did eventually find similar problems reported, but most of them were related to Japanese IMEs and have already been fixed in the JDK. I didn't find any reports specific to this Chinese IME, but I did find a workaround in case it's useful for others.
The brief summary is that I listen for the WM_IME_STARTCOMPOSITION Windows message. When I see that, I locate the IME candidate window, move it to the location I want, and override its WindowProc to prevent further moves. During composition I also listen for WM_KEYDOWN events because I no longer received any WM_IME messages while the user was composing, even though the candidate window closes and gets recreated several times throughout composition. When I receive the WM_IME_ENDCOMPOSITON message, I stop listening for WM_KEYDOWN messages.
As an alternative approach, I tried sending a WM_IME_CONTROL message with the IMC_SETCANDIDATEPOS command to move the candidate window, but this particular IME seems to ignore it.
I used JNA (https://github.com/twall/jna) to override the WindowProc on both the window containing my text area as well as the IME candidate window.
The code snippet below is an example of the workaround.
hwndMain = WIN_INSTANCE.FindWindow(null, "Main Window");
// Note the existing WindowProc so we can restore it later.
prevWndProc = new BaseTSD.LONG_PTR((long) WIN_INSTANCE.GetWindowLong(hwndMain, WinUser.GWL_WNDPROC));
// Register a new WindowProc that we will use to intercept IME messages.
mainListener = new WindowsCallbackListener() {
#Override
public int callback(int hWnd, int uMsg, int uParam, int lParam) {
if (uMsg == WM_IME_STARTCOMPOSITION || (imeComposing && uMsg == WM_KEYDOWN)) {
imeComposing = true;
final WinDef.HWND hwndIme = WIN_INSTANCE.FindWindow("SYSIME7_READING_UI", null);
if (hwndIme != null && !hwndIme.equals(imeWindow)) {
// We found an IME window that is not the same as the last one. We assume the last one was
// closed. We need to register our callback with the new window.
imeWindow = hwndIme;
final Point imeWindowLocation = getImeWindowLocation();
WIN_INSTANCE.MoveWindow(hwndIme, imeWindowLocation.x, imeWindowLocation.y, 0, 0, true);
final BaseTSD.LONG_PTR prevWndProcIme =
new BaseTSD.LONG_PTR((long) WIN_INSTANCE.GetWindowLong(hwndIme, WinUser.GWL_WNDPROC));
imeListener = new WindowsCallbackListener() {
#Override
public int callback(int hWnd, int uMsg, int uParam, int lParam) {
if (uMsg == WM_WINDOWPOSCHANGING) {
final WindowPosition pos = new WindowPosition(new Pointer((long)lParam));
pos.read();
pos.flags |= SWP_NOMOVE;
pos.write();
}
// Call the window's actual WndProc so the events get processed.
return WIN_INSTANCE.CallWindowProc(prevWndProcIme, hWnd, uMsg, uParam, lParam);
}
};
// Set the WndProc function to use our callback listener instead of the window's one.
WIN_INSTANCE.SetWindowLong(hwndIme, WinUser.GWL_WNDPROC, imeListener);
}
}
else if (uMsg == WM_IME_ENDCOMPOSITION) {
// We can discard the IME listener since its window is closed. If another one gets opened, we'll
// create a new listener.
imeListener = null;
imeComposing = false;
}
// Call the window's previous WindowProc so the event continues to get processed.
return WIN_INSTANCE.CallWindowProc(prevWndProc, hWnd, uMsg, uParam, lParam);
}
};
// Set the WindowProc function to use our WindowProc so the event continues to get processed.
WIN_INSTANCE.SetWindowLong(hwndMain, WinUser.GWL_WNDPROC, mainListener);
The code above assumes the following definitions:
private static final MyUser32 WIN_INSTANCE = MyUser32.INSTANCE;
private static final int SWP_NOMOVE = 2;
private static final int WM_KEYDOWN = 256;
private static final int WM_WINDOWPOSCHANGING = 70;
private static final int WM_IME_ENDCOMPOSITION = 270;
private static final int WM_IME_STARTCOMPOSITION = 269;
private WinDef.HWND hwndMain;
private BaseTSD.LONG_PTR prevWndProc;
// Keep references to these listeners so they don't get garbage-collected.
private WindowsCallbackListener mainListener;
private WindowsCallbackListener imeListener;
private boolean imeComposing;
private WinDef.HWND imeWindow;
public static class WindowPosition extends Structure {
public WinDef.HWND hwnd;
public WinDef.HWND hwndInsertAfter;
public int x;
public int y;
public int cx;
public int cy;
public int flags;
public WindowPosition(Pointer p) {
super(p);
}
#Override
protected List getFieldOrder() {
return Arrays.asList("hwnd", "hwndInsertAfter", "x", "y", "cx", "cy", "flags");
}
}
private interface MyUser32 extends User32 {
MyUser32 INSTANCE = (MyUser32) Native.loadLibrary("user32", MyUser32.class, W32APIOptions.DEFAULT_OPTIONS);
int CallWindowProc(BaseTSD.LONG_PTR prevWndProc, int hWnd, int uMsg, int uParam, int lParam);
int SetWindowLong(HWND hwnd, int nIndex, BaseTSD.LONG_PTR listener) throws LastErrorException;
int SetWindowLong(HWND hwnd, int nIndex, WindowsCallbackListener listener) throws LastErrorException;
}
private interface WindowsCallbackListener extends Callback, StdCall {
int callback(int hWnd, int Msg, int wParam, int lParam);
}
After some research on the same topic, I used the method "getVersion()" to check whether a JVM is 64 bit or 32 bit.
public static String getVersion()
{
String version = System.getProperty("sun.arch.data.model");
if (version != null && version.contains("64")){
return "64";
}else{
return "32";
}
}
It went wrong in some cases. Yes as the flag name mentions, the method is clearly sun-dependent. I tried getting the property "os.arch" also. But in some cases, it is wrongly identifying JVM.
Is there any more trustable way of checking the same?
My application is purely based on windows. And I don't want the method to work on any other platforms.
Here is a pure Java solution that checks the ability to link 32-bit library into the current process:
static boolean is64bitProcess() {
String wow64_kernel = System.getenv("systemroot") + "\\SysWOW64\\kernel32.dll";
if (new File(wow64_kernel).exists()) {
try {
System.load(wow64_kernel);
} catch (UnsatisfiedLinkError e) {
return true; // can not link 32-bit library into 64-bit process
}
}
return false;
}
You will need the 2 jar files from here :
https://java.net/projects/jna/downloads/directory/3.3.0
(Code edited to fix evaluation of IsWow64Process)
import com.sun.jna.platform.win32.Kernel32;
import com.sun.jna.platform.win32.WinNT;
import com.sun.jna.ptr.IntByReference;
public class Main {
public static void main(String[] args) {
System.out.println(is64BitJava());
}
private static boolean is64BitJava(){
if (!is64BitWindows()){
return false;
}
Kernel32 kernel32 = Kernel32.INSTANCE;
WinNT.HANDLE handle = kernel32.GetCurrentProcess();
if (!kernel32.IsWow64Process(handle, ref)){
return false;
}
return ref.getValue() == 0;
}
private static boolean is64BitWindows(){
String envVar = System.getenv("ProgramW6432");
if (envVar == null){
envVar = "";
}
return envVar.length() > 0;
}
}
In order to check if Windows is 64 bit, I check if ProgramW6432 environment variable is defined.
Then I use Win32 API GetCurrentProcess and IsWow64Process functions to examine current running process.
How to get the description text of an eclipse wizard using SWTBot? The wizard.shell.gettext() method gives the title, but I could not find any method for getting the description.I need it to verify the description and the error messages displayed on the wizard page.
as a workaround , I used this code
public void verifyWizardMessage(String message) throws AssertionError{
try{
bot.text(" "+message);
}catch(WidgetNotFoundException e){
throw (new AssertionError("no matching message found"));
}
}
here bot is a SWTBot instance available to method.The wizard messages automatically prepends a space to the description field,so I m using " "+message. Hope it helps
In order to test our eclipse plug-ins, the team I worked with developped a custom DSL on top of SWTBot to represent wizards, dialogs and so-on. Here is a code snippet that is working well in our case (beware that this might be eclipse version dependent, seems OK with eclipse 3.6 and 4.2)
class Foo {
/**
* The shell of your dialog/wizard
*/
private SWTBotShell shell;
protected SWTBotShell getShell() {
return shell;
}
protected <T extends Widget> T getTopLevelCompositeChild(final Class<T> clazz, final int index) {
return UIThreadRunnable.syncExec(shell.display, new Result<T>() {
#SuppressWarnings("unchecked")
public T run() {
Shell widget = getShell().widget;
if (!widget.isDisposed()) {
for (Control control : widget.getChildren()) {
if (control instanceof Composite) {
Composite composite = (Composite) control;
int counter = 0;
for (Control child : composite.getChildren()) {
if (clazz.isInstance(child)) {
if (counter == index) {
return (T) child;
}
++counter;
}
}
}
}
}
return null;
}
});
}
/**
* Returns the wizard's description or message displayed in its title dialog
* area.
*
* A wizard's description or message is stored in the very first Text widget
* (cf. <tt>TitleAreaDialog.messageLabel</tt> initialization in
* <tt>org.eclipse.jface.dialogs.TitleAreaDialog.createTitleArea(Composite)</tt>
* ).
*
*/
public String getDescription() {
final Text text = getTopLevelCompositeChild(Text.class, 0);
return UIThreadRunnable.syncExec(getShell().display, new Result<String>() {
public String run() {
if (text != null && !text.isDisposed()) {
return text.getText();
}
return null;
}
});
}
}
In case of WizardNewProjectCreationPage I use:
bot.textWithLabel("Create Project"); // This title set by page.setTitle("Create Project");
bot.text("Description."); // this is description set by page.setDescription("Description.");
Is it possible to avoid deprecation warning while compiling code with utility methods like:
public static void doSthForHorizontalSplitPanel(HorizontalSplitPanel hsp) {...}
and explicit declaration and/or creation of HorizontalSplitPanel widget, e.g.:
protected HorizontalSplitPanel main;
...
main = new HorizontalSplitPanel();
My goal is to eliminate these warnings without removing HorizontalSplitPanel usage, not giving global compiler flag (-Xlint:-deprecation) and with aid of minimal manual refactoring in terms of possible impact on code using HorizontalSplitPanel and my utility methods (i.e. as little code changes as possible).
Annotation #SuppressWarnings("deprecation") at method or class level seems not to work because of import HorizontalSplitPanel statements, replacement of deprecated HorizontalSplitPanel class in not an option (for now).
Is my goal possible to achieve at all? If so, what would be the best approach?
I'm using NetBeans 7.1, Java 1.6, GWT 2.3.
Standards mode and SplitlayoutPanel works better than the deprecated HorizontalSplitPanel.
Try this code you have to replace with HorizontalSplitPanel with HorizontalSplitLayoutPanel.
This code actually uses latest SplitLayoutPanel and the methods are equivalent to Deprecated HorizontalSplitPanel. The advantage is you don't have to change the code. Also pasted code for VerticalSplitPanel alternative VerticalSplitLayoutPanel. VerticalSplitLayoutPanel is unit tested and works fine.
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.ui.ScrollPanel;
import com.google.gwt.user.client.ui.SplitLayoutPanel;
import com.google.gwt.user.client.ui.Widget;
/**
* author: MKK
* Date: 4/29/13
* Time: 10:41 AM
* /**
*
* GWT depecrated HorizontalSplitpanel as of version 2.0.
* This is a wrapper class which extends new SplitLayoutPanel and has all the methods of deprecated old Splitpanels for
* seamless integration with existing code without breaking.
* Replacement of old HorizontalSplitLayoutPanel with new SplitLayoutPanel
*
*
*/
public class HorizontalSplitLayoutPanel extends SplitLayoutPanel{
private ScrollPanel leftScrollPanel = new ScrollPanel();
private ScrollPanel rightScrollPanel = new ScrollPanel();
private Widget leftWidget;
private Widget rightWidget;
public HorizontalSplitLayoutPanel(){
super();
init();
}
boolean dragged;
public void onBrowserEvent(Event event) {
super.onBrowserEvent(event);
switch (DOM.eventGetType(event)) {
case Event.ONMOUSEDOWN:
dragged = true;
break;
case Event.ONMOUSEUP:
dragged = false;
break;
case Event.ONMOUSEMOVE:
break;
}
}
public boolean isResizing(){
return dragged;
}
private void init() {
setSize("100%", "100%");
addWest(leftScrollPanel, 300);
add(rightScrollPanel);
sinkEvents(Event.ONMOUSEDOWN | Event.ONMOUSEUP );
}
public HorizontalSplitLayoutPanel(int splitterSize) {
super(splitterSize);
init();
}
public void setLeftWidget(Widget widget){
this.leftWidget = widget;
leftScrollPanel.clear();
leftScrollPanel.add(widget);
setSplitPosition("30%");
setWidgetToggleDisplayAllowed(leftScrollPanel,true);
}
public void setRightWidget(Widget widget){
try {
this.rightWidget = widget;
rightScrollPanel.clear();
rightScrollPanel.add(widget);
setSplitPosition("30%");
} catch (Exception e) {
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
}
}
public void removeWidget(Widget widget){
try {
if( leftScrollPanel.getWidget()== widget){
leftScrollPanel.remove(widget);
return;
}
rightScrollPanel.remove(widget);
} catch (Exception e) {
}
}
public void setSplitPosition(String percent){
if( percent.toLowerCase().indexOf("px") > -1){
percent = percent.replace("px","").trim();
int p = Integer.parseInt(percent);
setSplitPosition(p);
return;
}
percent = percent.replace("%","").trim();
int p = Integer.parseInt(percent);
double size = (getOffsetWidth()*p)/100.0;
if( p < 1.0 ){
size = 600;
}
setWidgetSize(leftScrollPanel, size);
}
public void setSplitPosition(int pixels){
setWidgetSize(leftScrollPanel, pixels);
}
public void hideLeftWidget() {
leftScrollPanel.clear();
setWidgetMinSize(leftScrollPanel,0);
}
public void showLeftWidget(){
leftScrollPanel.add(leftWidget);
}
public void hideRightWidget() {
rightScrollPanel.clear();
setWidgetMinSize(rightScrollPanel,0);
}
public void showRightWidget(){
rightScrollPanel.add(rightWidget);
}
}
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.ScrollPanel;
import com.google.gwt.user.client.ui.SplitLayoutPanel;
import com.google.gwt.user.client.ui.Widget;
/**
*
* GWT depecrated VerticalSplitPanel as of version 2.0.
* This is a wrapper class which extends new SplitLayoutPanel and has all the methods of deprecated old Splitpanels for
* seamless integration with existing code without breaking.
* Replacement of old VerticalSplitPanel with new SplitLayoutPanel
*
*
*/
public class VerticalSplitLayoutPanel extends SplitLayoutPanel {
private Widget topWidget;
private Widget bottomWidget;
private int offset=100;
private ScrollPanel topScrollPanel = new ScrollPanel();
private ScrollPanel bottomScrollPanel = new ScrollPanel();
public VerticalSplitLayoutPanel() {
super();
init();
}
public VerticalSplitLayoutPanel(int splitterSize) {
super(splitterSize);
init();
}
private void init() {
setSize("100%", "100%");
//int clientH = Window.getClientHeight()-offset;
// double size = clientH * 50/100;
addNorth(topScrollPanel, getOffsetHeight()/2.0);
add(bottomScrollPanel);
sinkEvents(Event.ONMOUSEDOWN | Event.ONMOUSEUP );
}
boolean dragged;
public void onBrowserEvent(Event event) {
super.onBrowserEvent(event);
switch (DOM.eventGetType(event)) {
case Event.ONMOUSEDOWN:
dragged = true;
break;
case Event.ONMOUSEUP:
dragged = false;
break;
case Event.ONMOUSEMOVE:
break;
}
}
public boolean isResizing(){
return dragged;
}
public void setTopWidget(Widget widget){
topScrollPanel.add(widget);
}
public void setBottomWidget(Widget widget){
bottomScrollPanel.add(widget);
}
public void removeWidget(Widget widget){
try {
if( topScrollPanel.getWidget()== widget){
topScrollPanel.remove(widget);
return;
}
bottomScrollPanel.remove(widget);
} catch (Exception e) {
}
}
public void setSplitPosition(String percent){
if( percent.toLowerCase().indexOf("px") > -1){
percent = percent.replace("px","").trim();
int p = Integer.parseInt(percent);
setSplitPosition(p);
return;
}
percent = percent.replace("%","").trim();
int p = Integer.parseInt(percent);
int oH = getOffsetHeight();
if( oH == 0 ){
oH = (Window.getClientHeight()-offset);
}
double h = (oH*p)/100.0;
setWidgetSize(topScrollPanel, h);
}
public void setSplitPosition(int pixels){
setWidgetSize(topScrollPanel, pixels);
}
public void setOffset(int size){
this.offset = size;
}
}
My approach is as follows.
Replace every usage of HorizontalSplitPanel with HorizontalSplitPanelWrapper defined below, then fix imports - this will eliminate import HorizontalSplitPanel and add import HorizontalSplitPanelWrapper. Done.
#SuppressWarnings("deprecation")
public class HorizontalSplitPanelWrapper implements IsWidget {
private Panel hsp = new com.google.gwt.user.client.ui.HorizontalSplitPanel();
public Widget asWidget() {
return hsp;
}
public com.google.gwt.user.client.Element getElement() {
return hsp.getElement();
}
public <H extends EventHandler> HandlerRegistration addHandler(final H handler, GwtEvent.Type<H> type) {
return hsp.addHandler(handler, type);
}
public boolean isResizing() {
return ((com.google.gwt.user.client.ui.HorizontalSplitPanel) hsp).isResizing();
}
public void setWidth(String width) {
hsp.setWidth(width);
}
public void setSplitPosition(String pos) {
((com.google.gwt.user.client.ui.HorizontalSplitPanel) hsp).setSplitPosition(pos);
}
public void add(IsWidget w) {
hsp.add(w);
}
}
Additional remarks.
My solution uses little trick with IsWidget interface from GWT - this minimizes code impact, because Widget can be substituted with implementation of IsWidget in most calls to GTW APIs.
Every method of HorizontalSplitPanel used in my code is now implemented by HorizontalSplitPanelWrapper and just delegated to internal HorizontalSplitPanel stored by hsp field.
There must be no declarations of fields and methods with HorizontalSplitPanel as type/param/result, as it will always yield deprecation warnings, regardless of #SuppressWarnings("deprecation"). Because of this, hsp field is declared as Panel.
If there are more methods of HorizontalSplitPanel used in rest of the code, there must be dummy delegator method in HorizontalSplitPanelWrapper for every one of them. Proper HorizontalSplitPanel object must be retrieved from field hsp with explicit cast in every such method.
That's it. No more deprecation warnings because of HorizontalSplitPanel, which still can be used.