GWT: In a TabLayoutPanel, how do I make tabs wrap if there are too many? - gwt

I'm using GWT 2.4. I have a TabLayoutPanel to which I add tabs. Each tab contains a ScrollPanel. My question is, how do I make the tabs in the tab bar wrap to the next line if the width of the tab bar exceeds the visible width?
Thanks, - Dave

GWT's TabLayoutPanel intentionally never wraps tabs. See lines 246-248 in TabLayoutPanel.java - (line 217 defines private static final int BIG_ENOUGH_TO_NOT_WRAP = 16384). You might be able to override this, but as #milan says, it's probably not good design.

Having multiple lines is, indeed, not recommended...
However, to be able to navigate left/right on a single tab bar with many tabs, you can use this recipe:
http://devnotesblog.wordpress.com/2010/06/17/scrollable-gwt-tablayoutpanel/
And an updated implementation that doesn't use the deprecated DeferredCommand:
package whatever.you.want;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.logical.shared.ResizeEvent;
import com.google.gwt.event.logical.shared.ResizeHandler;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.resources.client.ImageResource;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.LayoutPanel;
import com.google.gwt.user.client.ui.TabLayoutPanel;
import com.google.gwt.user.client.ui.Widget;
/**
* A {#link TabLayoutPanel} that shows scroll buttons if necessary
*/
public class ScrolledTabLayoutPanel extends TabLayoutPanel {
private static final int IMAGE_PADDING_PIXELS = 4;
private LayoutPanel panel;
private FlowPanel tabBar;
private Image scrollLeftButton;
private Image scrollRightButton;
private HandlerRegistration windowResizeHandler;
private ImageResource leftArrowImage;
private ImageResource rightArrowImage;
public ScrolledTabLayoutPanel(double barHeight, Unit barUnit,
ImageResource leftArrowImage, ImageResource rightArrowImage) {
super(barHeight, barUnit);
this.leftArrowImage = leftArrowImage;
this.rightArrowImage = rightArrowImage;
// The main widget wrapped by this composite, which is a LayoutPanel with the tab bar & the tab content
panel = (LayoutPanel) getWidget();
// Find the tab bar, which is the first flow panel in the LayoutPanel
for (int i = 0; i < panel.getWidgetCount(); ++i) {
Widget widget = panel.getWidget(i);
if (widget instanceof FlowPanel) {
tabBar = (FlowPanel) widget;
break; // tab bar found
}
}
initScrollButtons();
}
#Override
public void add(Widget child, Widget tab) {
super.add(child, tab);
checkIfScrollButtonsNecessary();
}
#Override
public boolean remove(Widget w) {
boolean b = super.remove(w);
checkIfScrollButtonsNecessary();
return b;
}
#Override
protected void onLoad() {
super.onLoad();
if (windowResizeHandler == null) {
windowResizeHandler = Window.addResizeHandler(new ResizeHandler() {
#Override
public void onResize(ResizeEvent event) {
checkIfScrollButtonsNecessary();
}
});
}
}
#Override
protected void onUnload() {
super.onUnload();
if (windowResizeHandler != null) {
windowResizeHandler.removeHandler();
windowResizeHandler = null;
}
}
private ClickHandler createScrollClickHandler(final int diff) {
return new ClickHandler() {
#Override
public void onClick(ClickEvent event) {
Widget lastTab = getLastTab();
if (lastTab == null)
return;
int newLeft = parsePosition(tabBar.getElement().getStyle().getLeft()) + diff;
int rightOfLastTab = getRightOfWidget(lastTab);
// Prevent scrolling the last tab too far away form the right border,
// or the first tab further than the left border position
if (newLeft <= 0 && (getTabBarWidth() - newLeft < (rightOfLastTab + 20))) {
scrollTo(newLeft);
}
}
};
}
/** Create and attach the scroll button images with a click handler */
private void initScrollButtons() {
scrollLeftButton = new Image(leftArrowImage);
int leftImageWidth = scrollLeftButton.getWidth();
panel.insert(scrollLeftButton, 0);
panel.setWidgetLeftWidth(scrollLeftButton, 0, Unit.PX, leftImageWidth, Unit.PX);
panel.setWidgetTopHeight(scrollLeftButton, 0, Unit.PX, scrollLeftButton.getWidth(), Unit.PX);
scrollLeftButton.addClickHandler(createScrollClickHandler(+20));
scrollLeftButton.setVisible(false);
scrollRightButton = new Image(rightArrowImage);
panel.insert(scrollRightButton, 0);
panel.setWidgetLeftWidth(scrollRightButton, leftImageWidth + IMAGE_PADDING_PIXELS, Unit.PX, scrollRightButton.getWidth(), Unit.PX);
panel.setWidgetTopHeight(scrollRightButton, 0, Unit.PX, scrollRightButton.getHeight(), Unit.PX);
scrollRightButton.addClickHandler(createScrollClickHandler(-20));
scrollRightButton.setVisible(false);
}
private void checkIfScrollButtonsNecessary() {
// Defer size calculations until sizes are available, when calculating immediately after
// add(), all size methods return zero
Scheduler.get().scheduleDeferred( new Scheduler.ScheduledCommand() {
#Override
public void execute() {
boolean isScrolling = isScrollingNecessary();
// When the scroll buttons are being hidden, reset the scroll position to zero to
// make sure no tabs are still out of sight
if (scrollRightButton.isVisible() && !isScrolling) {
resetScrollPosition();
}
scrollRightButton.setVisible(isScrolling);
scrollLeftButton.setVisible(isScrolling);
}
}
);
}
private void resetScrollPosition() {
scrollTo(0);
}
private void scrollTo(int pos) {
tabBar.getElement().getStyle().setLeft(pos, Unit.PX);
}
private boolean isScrollingNecessary() {
Widget lastTab = getLastTab();
if (lastTab == null)
return false;
return getRightOfWidget(lastTab) > getTabBarWidth();
}
private int getRightOfWidget(Widget widget) {
return widget.getElement().getOffsetLeft() + widget.getElement().getOffsetWidth();
}
private int getTabBarWidth() {
return tabBar.getElement().getParentElement().getClientWidth();
}
private Widget getLastTab() {
if (tabBar.getWidgetCount() == 0)
return null;
return tabBar.getWidget(tabBar.getWidgetCount() - 1);
}
private static int parsePosition(String positionString) {
int position;
try {
for (int i = 0; i < positionString.length(); i++) {
char c = positionString.charAt(i);
if (c != '-' && !(c >= '0' && c <= '9')) {
positionString = positionString.substring(0, i);
}
}
position = Integer.parseInt(positionString);
} catch (NumberFormatException ex) {
position = 0;
}
return position;
}
}

Related

Weex customized native android component Rich Text height issue

I am creating a Richtext(WXTextView.java) view component in Weex by extending WXComponent. As Richtext component is not available in weex android sdk and "v-html" tag is also not supported in weex text component.
When my Richtext element is wrapped inside a div, the element is not visible. I have to manually add height to its parent div to make it visible.
<div class="parent">
<textView
ref="nativeTextView"
:style="{
color: '#ff6600',
fontSize: '40px',
maxLine: 2,
borderWidth: 2,
borderStyle: 'solid',
borderColor: 'green',
}"
text="ABCDEF"
/>
</div>
Giving height to the parent doesn't solve my purpose because text length is dynamic. I want to make this behavior just like default weex text component supporting rich text.
WXTextView.java
public class WXTextView extends WXComponent<TextView> {
private WXVContainer mContainer;
private int mHeight;
public WXTextView(WXSDKInstance instance, WXDomObject dom, WXVContainer parent) {
super(instance, dom, parent);
mContainer = parent;
}
#Override
protected TextView initComponentHostView(#NonNull Context context) {
TextView textView = new TextView(context);
setProperty(WXComponent.PROP_FIXED_SIZE, WXComponent.PROP_FS_WRAP_CONTENT);
textView.setIncludeFontPadding(false);
textView.setTextSize(WXText.sDEFAULT_SIZE);
return textView;
}
#WXComponentProp(name = "text")
public void setText(String text) {
getHostView().setText(Html.fromHtml(text));
updateUI();
}
private void updateUI() {
ViewGroup.LayoutParams params = mContainer.getRealView().getLayoutParams();
params.height = getHeight();
mContainer.getRealView().setLayoutParams(params);
mContainer.getRealView().invalidate();
}
#WXComponentProp(name = "ellipsize")
public void setEllipsize(String positionString) {
try {
int position = Integer.parseInt(positionString);
TextUtils.TruncateAt truncateType;
switch (position) {
case 0:
truncateType = TextUtils.TruncateAt.START;
break;
case 1:
truncateType = TextUtils.TruncateAt.MIDDLE;
break;
default:
truncateType = TextUtils.TruncateAt.END;
break;
}
getHostView().setEllipsize(truncateType);
updateUI();
} catch (Exception exception) {
exception.printStackTrace();
}
}
#WXComponentProp(name = "maxLine")
public void setMaxLine(String lineString) {
try {
int lineCount = Integer.parseInt(lineString);
getHostView().setMaxLines(lineCount);
updateUI();
} catch (Exception exception) {
exception.printStackTrace();
}
}
#JSMethod
public void getElementSpecs(JSCallback callback){
Log.d("nikhil", "android getHeight: " + getHostView().getHeight());
Map<String, Object> data = new HashMap<>();
data.put("width", getHostView().getMeasuredWidth());
data.put("height", getHostView().getMeasuredHeight());
data.put("positionX", getHostView().getX());
data.put("positionY", getHostView().getY());
callback.invoke(data);
}
#WXComponentProp(name = "color")
public void setColor(String color) {
getHostView().setTextColor(Color.parseColor(color));
}
#WXComponentProp(name = "fontSize")
public void setFontSize(String sizeString) {
int lastIndex = sizeString.indexOf("px");
if (lastIndex == -1) {
lastIndex = sizeString.length();
}
sizeString = sizeString.substring(0, lastIndex);
int size = Integer.parseInt(sizeString);
getHostView().setTextSize(size);
updateUI();
}
public int getHeight() {
getHostView().setText(getHostView().getText());
getHostView().setTextSize(TypedValue.COMPLEX_UNIT_PX, getHostView().getTextSize());
int widthMeasureSpec = View.MeasureSpec.makeMeasureSpec(mContainer.getRealView().getLayoutParams().width,
View.MeasureSpec.AT_MOST);
int heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
getHostView().measure(widthMeasureSpec, heightMeasureSpec);
mHeight = getHostView().getMeasuredHeight();
return mHeight;
}
}
Text is the most complicated component in Weex. In order to achieve similar behavior like weex text, you need extend WXDomObject as text extend WXTextDomObject, and implement your own text measure function.
In fact, I have written a richtext component in weex which will be released soon.

How to create Drag n Drop for Cells in a DataGrid

I'm trying to add drag-n-drop to cell widgets. More specifically, I want to drag and drop ClickableTextCells, but they don't have the specific methods and not even .addDomHandler, so I can't just create something like .addDomHandler(new DragStartHandler() { ... }
Can someone give some help on how to DnD cells, preferably with pure GWT?
I do not know how to implement a DnD with GWT, but I know how to implement a CnC (Clic 'n Clic), which might interest you. DnD are cool, fun and beautiful, but I think that some times they are not very convenient. For instance if you have a big screen requiring a scroll, and if you want to DnD an item from the top to the bottom, it is not so convenient to have to hold the mouse... But this just a personnal feeling...
Anyway, I would recommand you to use a ListDataProvider along with simple events, in order to move your items: the first clic selects the source item, and the second clic selects the destination. Once the source and the destination are known, you can move your item.
It works well for me... and if you add some styles to highlight source and destination, it is very nice.
Example:
This is the main class:
import java.util.ArrayList;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.cellview.client.CellList;
import com.google.gwt.user.client.ui.DialogBox;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.FocusPanel;
import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.view.client.ListDataProvider;
import com.google.gwt.view.client.SelectionChangeEvent;
import com.google.gwt.view.client.SingleSelectionModel;
public class Clic_Clic {
private static final Integer LEFT = 0;
private static final Integer CENTER = 1;
private static final Integer RIGHT = 2;
private ClicClicSelectionModel<Item> selectionModel = new ClicClicSelectionModel<Item>();
ListDataProvider<Item> leftLDP = new ListDataProvider<Item>();
ListDataProvider<Item> centerLDP = new ListDataProvider<Item>();
ListDataProvider<Item> rightLDP = new ListDataProvider<Item>();
FocusPanel left = new FocusPanel(), center = new FocusPanel(), right = new FocusPanel();
Item selected = null;
public Clic_Clic() {
// --- Builds the GUI
DialogBox clic_clic = buildGUI();
clic_clic.center();
clic_clic.show();
// --- Initializes data
configureSelectionManagement();
initCellLists();
configureAddAndRemove();
// --- Fills the left LDP
leftLDP.getList().add(new Item("Fraternité", LEFT));
leftLDP.refresh();
// --- Fills the center LDP
centerLDP.getList().add(new Item("Egalité", LEFT));
centerLDP.refresh();
// --- Fills the right LDP
rightLDP.getList().add(new Item("Liberté", RIGHT));
rightLDP.refresh();
}
private DialogBox buildGUI() {
DialogBox db = new DialogBox();
db.setText("A simple example for Clic 'n Clic");
db.setSize("300px", "200px");
db.setGlassEnabled(true);
db.setModal(true);
FlowPanel fp = buildContent();
db.add(fp);
return db;
}
private FlowPanel buildContent() {
Grid g = new Grid(1, 3);
g.setSize("300px", "200px");
g.setWidget(0, 0, left);
left.setSize("100px", "100px");
left.getElement().getStyle().setBackgroundColor("blue");
g.setWidget(0, 1, center);
center.setSize("100px", "100px");
g.setWidget(0, 2, right);
right.setSize("100px", "100px");
right.getElement().getStyle().setBackgroundColor("red");
FlowPanel fp = new FlowPanel();
fp.setSize("300px", "200px");
fp.add(new Label("Do you know the correct order ?"));
fp.add(g);
return fp;
}
private void initCellLists() {
// --- Associates the left panel with the leftLDP ListDataProvider
CellList<Item> cellListLeft = new CellList<Item>(new MyCell());
cellListLeft.setSelectionModel(selectionModel);
left.add(cellListLeft);
leftLDP = new ListDataProvider<Item>(new ArrayList<Item>());
leftLDP.addDataDisplay(cellListLeft);
// --- Associates the center panel with the centerLDP ListDataProvider
CellList<Item> cellListCenter = new CellList<Item>(new MyCell());
cellListCenter.setSelectionModel(selectionModel);
center.add(cellListCenter);
centerLDP = new ListDataProvider<Item>(new ArrayList<Item>());
centerLDP.addDataDisplay(cellListCenter);
// --- Associates the right panel with the rightLDP ListDataProvider
CellList<Item> cellListRight = new CellList<Item>(new MyCell());
cellListRight.setSelectionModel(selectionModel);
right.add(cellListRight);
rightLDP = new ListDataProvider<Item>(new ArrayList<Item>());
rightLDP.addDataDisplay(cellListRight);
}
private void configureAddAndRemove() {
// --- If the user clic on the left
left.addClickHandler(new ClickHandler() {
#Override
public void onClick(ClickEvent event) {
if (selected != null) {
// --- If the selected item comes from the right
if (selected.getContainerIndex() == RIGHT) {
rightLDP.getList().remove(selected);
rightLDP.refresh();
selected.setContainerIndex(LEFT);
leftLDP.getList().add(selected);
leftLDP.refresh();
selected = null;
}
// --- If the selected item comes from the center
if (selected.getContainerIndex() == CENTER) {
centerLDP.getList().remove(selected);
centerLDP.refresh();
selected.setContainerIndex(LEFT);
leftLDP.getList().add(selected);
leftLDP.refresh();
selected = null;
}
}
}
});
// --- If the user clic on the center
center.addClickHandler(new ClickHandler() {
#Override
public void onClick(ClickEvent event) {
if (selected != null) {
// --- If the selected item comes from the right
if (selected.getContainerIndex() == RIGHT) {
rightLDP.getList().remove(selected);
rightLDP.refresh();
selected.setContainerIndex(CENTER);
centerLDP.getList().add(selected);
centerLDP.refresh();
selected = null;
}
// --- If the selected item comes from the left
if (selected.getContainerIndex() == LEFT) {
leftLDP.getList().remove(selected);
leftLDP.refresh();
selected.setContainerIndex(CENTER);
centerLDP.getList().add(selected);
centerLDP.refresh();
selected = null;
}
}
}
});
// --- If the user clic on the right
right.addClickHandler(new ClickHandler() {
#Override
public void onClick(ClickEvent event) {
// --- If the selected item comes from the left
if (selected.getContainerIndex() == LEFT) {
leftLDP.getList().remove(selected);
leftLDP.refresh();
selected.setContainerIndex(RIGHT);
rightLDP.getList().add(selected);
rightLDP.refresh();
selected = null;
}
// --- If the selected item comes from the center
if (selected.getContainerIndex() == CENTER) {
centerLDP.getList().remove(selected);
centerLDP.refresh();
selected.setContainerIndex(RIGHT);
rightLDP.getList().add(selected);
rightLDP.refresh();
selected = null;
}
}
});
}
#SuppressWarnings("hiding")
class ClicClicSelectionModel<Item> extends SingleSelectionModel<Item> {
#Override
public void setSelected(Item object, boolean selected) {
if (getSelectedObject() != null && getSelectedObject().equals(object)) {
super.setSelected(object, !selected);
} else {
super.setSelected(object, selected);
}
};
}
private void configureSelectionManagement() {
selectionModel.addSelectionChangeHandler(new SelectionChangeEvent.Handler() {
#Override
public void onSelectionChange(SelectionChangeEvent event) {
Item selected = selectionModel.getSelectedObject();
updateSelected(selected);
}
});
}
private void updateSelected(Item item) {
// --- If no item has been selected, update the selected item
if (selected == null) {
selected = item;
}
}
}
You also need this one:
public class Item {
private String label;
private Integer containerIndex;
public Item(String l, Integer id) {
this.label = l;
this.containerIndex = id;
}
public String getLabel() {
return label;
}
public void setLabel(String label) {
this.label = label;
}
public Integer getContainerIndex() {
return containerIndex;
}
public void setContainerIndex(Integer containerIndex) {
this.containerIndex = containerIndex;
}
}
And finally, this one:
import com.google.gwt.cell.client.AbstractCell;
import com.google.gwt.safehtml.shared.SafeHtml;
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
import com.google.gwt.safehtml.shared.SafeHtmlUtils;
public class MyCell extends AbstractCell<Item> {
#Override
public void render(com.google.gwt.cell.client.Cell.Context context, Item value, SafeHtmlBuilder sb) {
if (value != null) {
// --- If the value comes from the user, we escape it to avoid XSS
// attacks.
SafeHtml safeValue = SafeHtmlUtils.fromString(value.getLabel());
sb.append(safeValue);
}
}
}
Here you go.
Next time I'll try to make a fun example :)
Hope it helps.

How to add a JDialog in to desktop pane or just call from an internal frame

I am working on a project that uses MDI form in java. I have created a frame and then added a desktop pane to it. My project uses lot of internal frames. Also those internal frames require to show custom dialogs that i have created on my own. for it to be clear, i say, one jdialog has a table asking the user to select one row. but the problem is when i call the jdialog from the internal frame (with modality=true), the dialog is show on the top of main frame and not just on the top of internal frame. This makes it impossible to minimize the window when the jdialog is showing.
In my view there are 2 possible solutions (which may not possible!!).. Either the jdialog should be shown inside the dektop pane or i should create an internal frame instead of jdialog and make it appear to be modal to the parent internal frame. i.e, when i want to show the dialog, i may disable the internal frame and set the form unable to focus and then show a new internal frame on the top of this internal frame. I have been searching the forums for weeks.. but i couldn't find an answer. I hope you would have a solution. Thanks in advance, sir.
I also had the same problem, while working on a java project that works quite fine in java 6 but shown the same problem when changed to java7.
I found a solution.
I added a
dialog.setVisible(false) followed by a dialog.setVisible(true).
Then the dialog is responding to keyboard.
I am also working on an MDI app that uses a lof internal frames which show custom dialogs. I make my dialogs non-modal so that the internal frames can be iconified and/or the whole desktoppane can be minimized while the dialogs remain visible.
If you absolutely need modal behavior (i.e., you want to require the user to interact with a dialog before doing anything else) perhaps you can leave the dialog modeless but code in de facto modality.
Also, have you looked at the behavior of
setModalityType(java.awt.Dialog.ModalityType.DOCUMENT_MODAL);
?
Wow!! I got the answer from webbyt... Just avoid using internal frames.. try using the class ModalityInternalFrame (subclass of JinternalFrame).. and everything works fine.. Here is the class
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyVetoException;
import java.beans.VetoableChangeListener;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.*;
import javax.swing.event.InternalFrameAdapter;
import javax.swing.event.InternalFrameEvent;
/**
* An extended
* <code>JInternalFrame</code> that provides modality in a child/parent
* hierarchy
*
* #author webbyit
*/
public class ModalityInternalFrame extends JInternalFrame {
protected JDesktopPane desktopPane;
protected JComponent parent;
protected ModalityInternalFrame childFrame;
protected JComponent focusOwner;
private boolean wasCloseable;
public ModalityInternalFrame() {
init(); // here to allow netbeans to use class in gui builder
}
public ModalityInternalFrame(JComponent parent) {
this(parent, null);
}
public ModalityInternalFrame(JComponent parent, String title) {
this(parent, title, false);
}
public ModalityInternalFrame(JComponent parent, String title, boolean resizable) {
this(parent, title, resizable, false);
}
public ModalityInternalFrame(JComponent parent, String title, boolean resizable, boolean closeable) {
this(parent, title, resizable, closeable, false);
}
public ModalityInternalFrame(JComponent parent, String title, boolean resizable, boolean closeable,
boolean maximizable) {
this(parent, title, resizable, closeable, maximizable, false);
}
public ModalityInternalFrame(JComponent parent, String title, boolean resizable, boolean closeable,
boolean maximizable,
boolean iconifiable) {
super(title, resizable, closeable, maximizable, iconifiable);
setParentFrame(parent);
//setFocusTraversalKeysEnabled(false);
if (parent != null && parent instanceof ModalityInternalFrame) {
((ModalityInternalFrame) parent).setChildFrame(ModalityInternalFrame.this);
/*
* set focus to the new frame and show the frame Code added by Jasir
*/
try {
((ModalityInternalFrame) parent).setSelected(false);
setSelected(true);
setVisible(true);
} catch (PropertyVetoException ex) {
Logger.getLogger(ModalityInternalFrame.class.getName()).log(Level.SEVERE, null, ex);
}
}
// Add glass pane
ModalityInternalGlassPane glassPane = new ModalityInternalGlassPane(this);
setGlassPane(glassPane);
// Add frame listeners
addFrameListener();
// Add frame veto listenr
addFrameVetoListener();
init();
// calculate size and position
}
private void setParentFrame(JComponent parent) {
desktopPane = JOptionPane.getDesktopPaneForComponent(parent);
this.parent = parent == null ? JOptionPane.getDesktopPaneForComponent(parent) : parent; // default to desktop if no parent given
}
public JComponent getParentFrame() {
return parent;
}
public void setChildFrame(ModalityInternalFrame childFrame) {
this.childFrame = childFrame;
}
public ModalityInternalFrame getChildFrame() {
return childFrame;
}
public boolean hasChildFrame() {
return (childFrame != null);
}
protected void addFrameVetoListener() {
addVetoableChangeListener(new VetoableChangeListener() {
public void vetoableChange(PropertyChangeEvent evt) throws PropertyVetoException {
if (evt.getPropertyName().equals(JInternalFrame.IS_SELECTED_PROPERTY)
&& evt.getNewValue().equals(Boolean.TRUE)) {
if (hasChildFrame()) {
//childFrame.setSelected(true);
if (childFrame.isIcon()) {
childFrame.setIcon(false);
}
throw new PropertyVetoException("no!", evt);
}
}
}
});
}
/**
* Method to control the display of the glass pane, dependant on the frame
* being active or not
*/
protected synchronized void addFrameListener() {
addInternalFrameListener(new InternalFrameAdapter() {
#Override
public void internalFrameActivated(InternalFrameEvent e) {
if (hasChildFrame() == true) {
getGlassPane().setVisible(true);
grabFocus();
} else {
getGlassPane().setVisible(false);
}
}
#Override
public void internalFrameOpened(InternalFrameEvent e) {
getGlassPane().setVisible(false);
try {
setSelected(true);
} catch (PropertyVetoException ex) {
Logger.getLogger(ModalityInternalFrame.class.getName()).log(Level.SEVERE, null, ex);
}
}
#Override
public void internalFrameClosing(InternalFrameEvent e) {
if (parent != null && parent instanceof ModalityInternalFrame) {
((ModalityInternalFrame) parent).childClosing();
}
}
});
}
/**
* Method to handle child frame closing and make this frame available for
* user input again with no glass pane visible
*/
protected void childClosing() {
setClosable(wasCloseable);
getGlassPane().setVisible(false);
if (focusOwner != null) {
java.awt.EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
moveToFront();
setSelected(true);
focusOwner.grabFocus();
} catch (PropertyVetoException ex) {
}
}
});
focusOwner.grabFocus();
}
getGlassPane().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
setChildFrame(null);
getDesktopPane().setSelectedFrame(this);
System.out.println(getDesktopPane().getSelectedFrame());
}
/*
* Method to handle child opening and becoming visible.
*/
protected void childOpening() {
// record the present focused component
wasCloseable = isClosable();
setClosable(false);
focusOwner = (JComponent) getMostRecentFocusOwner();
grabFocus();
getGlassPane().setVisible(true);
getGlassPane().setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
}
#Override
public void show() {
if (parent != null && parent instanceof ModalityInternalFrame) {
// Need to inform parent its about to lose its focus due
// to child opening
((ModalityInternalFrame) parent).childOpening();
}
calculateBounds();
super.show();
}
protected void init() {
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
getContentPane().setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING).addGap(0, 394, Short.MAX_VALUE));
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING).addGap(0, 274, Short.MAX_VALUE));
pack();
}
public void calculateBounds() {
Dimension frameSize = getPreferredSize();
Dimension parentSize = new Dimension();
Dimension rootSize = new Dimension(); // size of desktop
Point frameCoord = new Point();
if (desktopPane != null) {
rootSize = desktopPane.getSize(); // size of desktop
frameCoord = SwingUtilities.convertPoint(parent, 0, 0, desktopPane);
parentSize = parent.getSize();
}
//setBounds((rootSize.width - frameSize.width) / 2, (rootSize.height - frameSize.height) / 2, frameSize.width, frameSize.height);
// We want dialog centered relative to its parent component
int x = (parentSize.width - frameSize.width) / 2 + frameCoord.x;
int y = (parentSize.height - frameSize.height) / 2 + frameCoord.y;
// If possible, dialog should be fully visible
int ovrx = x + frameSize.width - rootSize.width;
int ovry = y + frameSize.height - rootSize.height;
x = Math.max((ovrx > 0 ? x - ovrx : x), 0);
y = Math.max((ovry > 0 ? y - ovry : y), 0);
setBounds(x, y, frameSize.width, frameSize.height);
}
/**
* Glass pane to overlay. Listens for mouse clicks and sets selected on
* associated modal frame. Also if modal frame has no children make class
* pane invisible
*/
class ModalityInternalGlassPane extends JComponent {
private ModalityInternalFrame modalFrame;
public ModalityInternalGlassPane(ModalityInternalFrame frame) {
modalFrame = frame;
addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
if (modalFrame.isSelected() == false) {
try {
modalFrame.setSelected(true);
if (modalFrame.hasChildFrame() == false) {
setVisible(false);
}
} catch (PropertyVetoException e1) {
//e1.printStackTrace();
}
}
}
});
}
#Override
public void paint(Graphics g) {
super.paint(g);
g.setColor(new Color(255, 255, 255, 100));
g.fillRect(0, 0, getWidth(), getHeight());
}
}
}
But there are some problems still with focus and something else..

How to make GWT Datagrid have its first column fixed and scroll horizontally and vertically

Currently GWT DataGrid header does this trick with a fixed header row during a vertical scroll. Is there a way to acheive the same on an entire (first) column?
I have implemented ScrolledGrid that freezes first column in DataGrid. You need to use it instead of DataGrid in order to make first column be frozen.
import com.google.gwt.dom.client.*;
import com.google.gwt.event.dom.client.ScrollEvent;
import com.google.gwt.event.dom.client.ScrollHandler;
import com.google.gwt.user.cellview.client.DataGrid;
import com.google.gwt.user.client.ui.HeaderPanel;
import com.google.gwt.user.client.ui.ScrollPanel;
/**
*
* #author Yuri Plaksyuk
*/
public class ScrolledGrid extends DataGrid {
private final Text cssText;
private boolean addedClass = false;
private int currentScrollLeft = 0;
public ScrolledGrid() {
cssText = Document.get().createTextNode("");
StyleElement styleElement = Document.get().createStyleElement();
styleElement.setType("text/css");
styleElement.appendChild(cssText);
HeaderPanel headerPanel = (HeaderPanel) getWidget();
headerPanel.getElement().insertFirst(styleElement);
final ScrollPanel scrollPanel = (ScrollPanel) headerPanel.getContentWidget();
scrollPanel.addScrollHandler(new ScrollHandler() {
#Override
public void onScroll(ScrollEvent event) {
int scrollLeft = scrollPanel.getHorizontalScrollPosition();
if (scrollLeft != currentScrollLeft) {
StringBuilder css = new StringBuilder();
if (scrollLeft > 0) {
css.append(".ScrolledGrid-frozen {");
css.append("background-color: inherit;");
css.append("}");
css.append(".ScrolledGrid-frozen div {");
css.append("position: absolute;");
css.append("left: ").append(scrollLeft).append("px;");
css.append("width: ").append(getColumnWidth(getColumn(0))).append(";");
css.append("padding-left: 1.3em;");
css.append("padding-right: 0.5em;");
css.append("margin-top: -0.7em;");
css.append("white-space: nowrap;");
css.append("background-color: inherit;");
css.append("}");
}
else
css.append(".ScrolledGrid-frozen { }");
css.append("th.ScrolledGrid-frozen { background-color: white; }");
cssText.setData(css.toString());
if (!addedClass) {
NodeList<TableRowElement> rows;
TableRowElement row;
TableCellElement cell;
rows = getTableHeadElement().getRows();
for (int i = 0; i < rows.getLength(); ++i) {
row = rows.getItem(i);
cell = row.getCells().getItem(0);
cell.setInnerHTML("<div>" + cell.getInnerHTML() + "</div>");
cell.addClassName("ScrolledGrid-frozen");
}
rows = getTableBodyElement().getRows();
for (int i = 0; i < rows.getLength(); ++i) {
row = rows.getItem(i);
cell = row.getCells().getItem(0);
cell.addClassName("ScrolledGrid-frozen");
}
addedClass = true;
}
currentScrollLeft = scrollLeft;
}
}
});
}
}
Unfortunately, some CSS values are hard-coded.
I adapted Yuri's solution to achieve the following goals:
does not flicker
copes with arbitrary row-heights
works with SelectionModel
more uniform solution
It does not mess with the columns itself, but instead shows arbitrary "frozen" information on row-level.
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.dom.client.*;
import com.google.gwt.event.dom.client.ScrollEvent;
import com.google.gwt.event.dom.client.ScrollHandler;
import com.google.gwt.user.cellview.client.DataGrid;
import com.google.gwt.user.cellview.client.DefaultCellTableBuilder;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.ui.HeaderPanel;
import com.google.gwt.user.client.ui.ScrollPanel;
/**
* #author Daniel Lintner
*
* A DataGrid extension with the ability to display some row-level-information
* when scrolling left (horizontal), hence important columns out of sight of the user.
*/
public class FrozenDataGrid extends DataGrid
{
//textnode getting updated dynamically when scolling horizontally
private Text cssText;
//the latest scroll-position
private int currentScrollLeft = 0;
//an object extracting String-info from your rowdata
private FrozenValueProvider valueProvider;
//inject basic styling into the document - once
//this is how the frozen row-info looks like
static
{
Text baseCss = Document.get().createTextNode("");
StyleElement styleElement = Document.get().createStyleElement();
styleElement.setType("text/css");
styleElement.appendChild(baseCss);
StringBuilder css = new StringBuilder();
css.append(".ScrolledGrid-base {");
css.append("position: absolute;");
css.append("background-color: gray;");
css.append("padding: .3em;");
css.append("padding-left: .5em;");
css.append("padding-right: .5em;");
css.append("border-radius: 3px 3px;");
css.append("transition: opacity 500ms;");
css.append("color: white;");
css.append("margin-top: 2px;");
css.append("white-space: nowrap;");
css.append("}");
baseCss.setData(css.toString());
Document.get().getBody().insertFirst(styleElement);
}
public FrozenDataGrid()
{
super();
init();
}
public FrozenDataGrid(int pageSize, DataGrid.Resources resources)
{
super(pageSize, resources);
init();
}
public void init()
{
//create a css textnode
cssText = Document.get().createTextNode("");
//create dynamic css Style
StyleElement styleElement = Document.get().createStyleElement();
styleElement.setType("text/css");
styleElement.appendChild(cssText);
//append the initial style condition
//todo the name of this style might be built dynamically per instance - if multiple grid-instances exist/not the use-case by now
StringBuilder css = new StringBuilder();
css.append(".ScrolledGrid-frozen {");
css.append("opacity:0;");
css.append("}");
cssText.setData(css.toString());
//set a custom CellTableBuilder in order to inject the info-div to the row
setTableBuilder(new DefaultCellTableBuilder(this)
{
#Override
public void buildRowImpl(final Object rowValue, final int absRowIndex)
{
//do what DefaultCellTableBuilder does
super.buildRowImpl(rowValue, absRowIndex);
//only do something if there is a valueProvider
if(valueProvider != null) {
//we do this deferred because this row has to created first in order to access it
Scheduler.get().scheduleDeferred(new Scheduler.ScheduledCommand()
{
#Override
public void execute()
{
createInfoDiv(getTableBodyElement().getRows().getItem(absRowIndex % getPageSize()), rowValue);
}
});
}
}
});
//fetch the ScrollPanel from the grid
HeaderPanel headerPanel = (HeaderPanel) getWidget();
headerPanel.getElement().insertFirst(styleElement);
final ScrollPanel scrollPanel = (ScrollPanel) headerPanel.getContentWidget();
//setup a timer handling the left-offset-css thing
//we use a timer to be able to cancel this operation -> e.g. continuous scroll
final Timer timer = new Timer(){
#Override
public void run() {
StringBuilder css = new StringBuilder();
//we need to left-offset the info-divs
if (scrollPanel.getHorizontalScrollPosition() > 100)
{
css.append(".ScrolledGrid-frozen {");
css.append("left: ").append(3 + scrollPanel.getHorizontalScrollPosition()).append("px;");
css.append("opacity: 1;");
css.append("}");
}
//we are close to the leftmost scroll position: info hidden
else
{
css.append(".ScrolledGrid-frozen {");
css.append("opacity:0;");
css.append("}");
}
cssText.setData(css.toString());
}
};
//track scrolling
scrollPanel.addScrollHandler(new ScrollHandler()
{
#Override
public void onScroll(ScrollEvent event)
{
//cancel previous actions to scroll events
if(timer.isRunning())
timer.cancel();
//actual horizontal scrollposition
int scrollLeft = scrollPanel.getHorizontalScrollPosition();
//a horizontal scroll takes places
if (scrollLeft != currentScrollLeft)
{
//first we hide the row-info
StringBuilder css = new StringBuilder();
css.append(".ScrolledGrid-frozen {");
css.append("opacity:0;");
css.append("}");
cssText.setData(css.toString());
//render left offset after a delay
timer.schedule(500);
//remember the current horizontal position
currentScrollLeft = scrollLeft;
}
}
});
}
private void createInfoDiv(TableRowElement row, Object value)
{
//create a div element and add value and style to it
DivElement div = Document.get().createDivElement();
div.setInnerText(valueProvider.getFrozenValue(value));
div.addClassName("ScrolledGrid-base");
div.addClassName("ScrolledGrid-frozen");
//we add it to the first child of the row, because added as child of the row directly
// confuses the CellTable with coordinating header positions
row.getFirstChildElement().insertFirst(div);
}
public void setFrozenValueProvider(FrozenValueProvider valueProvider) {
this.valueProvider = valueProvider;
}
public interface FrozenValueProvider<T>{
String getFrozenValue(T data);
}
}
Hope this helps developers on this rarely and unsatisfactorily solved problem.
And... there is still room for improvement left.
Cheers Dan

GWT FlexTable - drag selection how?

I am trying to get a proper method for days to select multiple cells in a flextable's column.
So far i only managed to do it with clicks which works well, but a drag selection would be much better. I have been reading docs and searching, but all the stuff i found was based on deprecated code. I use GWT 2.0 .
I know i need some event handler which would run when drag selection mouse gesture occurs, and that handler needs to know the cell's index where the selection start and of course the cell's index where the selection ends.
Any advice || code would be much appreciated.
This needs to be improved but it should give you the basic idea. First you need to create a CustomTable that listens to MouseEvents. You can do this by extending composite to wrap a focuspanel and a flextable as such :
public class CustomTable extends Composite implements MouseDownHandler, MouseMoveHandler, MouseUpHandler{
List<CellWidget> widgets = new ArrayList<CellWidget>();
FlexTable table = new FlexTable();
FocusPanel focusPanel = new FocusPanel();
boolean selecting= false;
Point selectStart,selectEnd;
public CustomTable(){
focusPanel.setWidget(table);
focusPanel.addMouseDownHandler(this);
focusPanel.addMouseMoveHandler(this);
focusPanel.addMouseUpHandler(this);
initWidget(focusPanel);
}
public void setWidget(int row, int column, CellWidget widget){
widgets.add(widget);
table.setWidget(row, column, widget);
}
#Override
public void onMouseUp(MouseUpEvent event) {
event.preventDefault();
if (selecting){
selecting=false;
DOM.releaseCapture(this.getElement());
selectEnd = new Point(event.getClientX(),event.getClientY());
for (CellWidget widget : widgets){
if (widget.isIn(selectStart,selectEnd))
widget.say();
}
selectStart = selectEnd = null;
}
}
#Override
public void onMouseMove(MouseMoveEvent event) {
event.preventDefault();
if (selecting){
//do some fancy layout
}
}
#Override
public void onMouseDown(MouseDownEvent event) {
event.preventDefault();
selecting = true;
DOM.setCapture(this.getElement());
selectStart = new Point(event.getClientX(),event.getClientY());
}
}
Next you define a CellWidget which basically encapsulates what you would like to add to your cells. When added to DOM, CellWidget calculates and stores its position later to determine if it is in the selected area :
public class CellWidget extends Composite{
Widget content;
Point topLeft,topRight,bottomLeft,bottomRight;
public CellWidget(Widget w){
this.content = w;
initWidget(w);
}
#Override
protected void onLoad() {
topLeft = new Point(getAbsoluteLeft(),getAbsoluteTop());
topRight = new Point(getAbsoluteLeft()+getOffsetWidth(),getAbsoluteTop());
bottomLeft = new Point(getAbsoluteLeft(),getAbsoluteTop()+getOffsetHeight());
bottomRight = new Point(getAbsoluteLeft()+getOffsetWidth(),getAbsoluteTop()+getOffsetHeight());
}
public void say(){
Window.alert(content + " is selected!");
}
public boolean isIn(Point start, Point end){
if (topLeft.isBetween(start, end) || topRight.isBetween(start, end)
|| bottomLeft.isBetween(start, end) || bottomRight.isBetween(start, end))
return true;
else
return false;
}
}
A simple point implementation to make things easier :
public class Point {
int x,y;
public Point(int x,int y){
this.x=x;
this.y=y;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
#Override
public String toString() {
return x+","+y;
}
public boolean isBetween(Point p1,Point p2){
if (p1.getX() < x && p2.getX() > x && p1.getY() < y && p2.getY() > y)
return true;
return false;
}
}
Finally at your EntryPoint module you wrap things up by :
public void onModuleLoad() {
RootPanel rootPanel = RootPanel.get();
CustomTable table = new CustomTable();
table.setWidget(0, 0, new CellWidget(new Label("hello 0,0")));
table.setWidget(0, 1, new CellWidget(new Label("hello 0,1")));
table.setWidget(1, 0, new CellWidget(new Label("hello 1,0")));
table.setWidget(1, 1, new CellWidget(new Label("hello 1,1")));
rootPanel.add(table);
}
I know that the actual logic to determine if the widgets fall within the selected area is incomplete and needs to be improved but i think this solution is clear enough to give the basic idea. Cheers