GWT widget is breaking apart when dragged and dropped with gwt-dnd - java

So, I am having a very difficult time explaining my problem, I hope this is clear. Please tell me if I can clarify anything a little bit better!
I have a GWT web application that is using gwt-dnd to drag and drop widgets. I have Notecard objects (like 3x5 note cards) that extend AbsolutePanel and have a title and number displayed on them.
I have a vertical InsertPanel (a gwt FlowPanel) that Notecard objects are inserted into, one above the next (order matters here). Say there are 5 Notecards contained in the InsertPanel, all aligned vertically. If I pick one of them up, move it to a different index in the InsertPanel, and drop it, the body of the Notecard is dropped where it is supposed to be, but then the title and number are separated from the body of the Notecard and are pushed all the way up to the top of the InsertPanel. If I continue to move Notecards around, all the Notecard bodies function as planed, but all the titles and numbers are stacked on top of each other at the very top of the InsertPanel.
Is there something that I am missing that "glues" the Notecard widget components together? I tried having Notecard extend Composite instead of AbsolutePanel, but I am getting the same behavior.
For reference, I have attached the involved Notecard class below:
Notecard.java:
public class Notecard extends AbsolutePanel implements HasAllMouseHandlers {
private Long ID;
private String storyTitle;
private int points;
private Button dragHandleButton;
private AbstractProject project;
/*
* Constructors
*/
public Notecard( Long Id, String ttl, int pts ) {
this.ID = Id;
this.storyTitle = ttl;
this.points = pts;
setStyleName("Notecard-Wrapper");
setSize("100px", "60px");
Label titleLabel = new Label(storyTitle);
titleLabel.setStyleName("Notecard-TitleLabel");
add(titleLabel, 0, 0);
titleLabel.setSize("96px", "28px");
Label pointsLabel = new Label("" + points);
pointsLabel.setStyleName("Notecard-PointsLabel");
add(pointsLabel, 0, 35);
pointsLabel.setSize("100px", "20px");
dragHandleButton = new Button("");
dragHandleButton.setText("");
dragHandleButton.setStyleName("dragHandleButton");
add(dragHandleButton, 0, 0);
dragHandleButton.setSize("100px", "60px");
Button addTaskButton = new Button("+");
addTaskButton.addClickHandler(new ClickHandler() {
public void onClick(ClickEvent event) {
popupAddTaskPopup();
}
});
addTaskButton.setStyleName("Notecard-AddTaskButton");
addTaskButton.setText("+");
add(addTaskButton, 0, 40);
addTaskButton.setSize("20px", "20px");
}
public void popupAddTaskPopup() {
project.popupAddTaskPopupPanel( ID );
}
/*
* GETTERS & SETTERS
*/
public String getStoryTitle() {
return storyTitle;
}
public void setStoryTitle(String title) {
this.storyTitle = title;
}
public int getPoints() {
return points;
}
public void setPoints(int points) {
this.points = points;
}
public Long getID() {
return ID;
}
public Button getDragHandle() {
return dragHandleButton;
}
/*
* Implementation for HasAllMouseHandlers
*/
#Override
public HandlerRegistration addMouseDownHandler(MouseDownHandler handler) {
return dragHandleButton.addMouseDownHandler(handler);
}
#Override
public HandlerRegistration addMouseUpHandler(MouseUpHandler handler) {
return dragHandleButton.addMouseUpHandler(handler);
}
#Override
public HandlerRegistration addMouseOutHandler(MouseOutHandler handler) {
return dragHandleButton.addMouseOutHandler(handler);
}
#Override
public HandlerRegistration addMouseOverHandler(MouseOverHandler handler) {
return dragHandleButton.addMouseOverHandler(handler);
}
#Override
public HandlerRegistration addMouseMoveHandler(MouseMoveHandler handler) {
return dragHandleButton.addMouseMoveHandler(handler);
}
#Override
public HandlerRegistration addMouseWheelHandler(MouseWheelHandler handler) {
return dragHandleButton.addMouseWheelHandler(handler);
}
I am also using a drop controller that I implemented to handle the onDrop() event. But it just uses the AbstractInsertPanelDropController implementation for onDrop() and then adds some functionality for persistence mechanisms.

I figured out the problem. Turns out it was not at all a GWT or drag and drop thing. It was a CSS issue. My Notecard class, you will notice, is made up of an AbsolutePanel and then its child elements are all positioned absolutely. For the original drawing of the widget, GWT is smart enough to make the child elements be placed inside of the Notecard's wrapper (the AbsolutePanel). But when the Notecard was drag and dropped, it was just standard js, not GWT. Thus, the CSS properties for the child widgets that said:
/* CSS for child widgets */
position: absolute;
left: 0;
top: 0;
were no longer relative to the Notecard wrapper, but instead to the RootPanel. All I did was add a CSS style to the Notecard wrapper to make sure its children were relative to it:
/* CSS for Notecard-Wrapper */
position: relative;
and then everything behaved great.
I figured this out using Firebug for Firefox and then Google'ing around about the CSS issue.

Related

Referencing a java class in its own constructor

I'm building a Java Swing class called ListView that attempts to be a general purpose list.
public class ListView<T> extends JPanel {
private IListViewDataSource<T> dataSource;
private JPanel list;
public ListView(IListViewDataSource<T> dataSource, Dimension dimension) {
this.dataSource = dataSource;
list = new JPanel(new GridLayout(0, 1));
this.add(new JScrollPane(list));
this.setPreferredSize(dimension);
}
public void loadRows() {
for (int i = 0; i < dataSource.getNumberOfElements(); i++) {
JLabel label = new JLabel(dataSource.getTitleOfElement(dataSource.getElementAtPosition(i)));
list.add(label);
}
}
}
In order to do this, I declared an interface called IListViewDataSource that defines the methods required for the list view to obtain its data.
public interface IListViewDataSource<T> {
T getElementAtPosition(int position);
int getNumberOfElements();
String getTitleOfElement(T element);
}
I wanted it to be possible to instantiate a new ListView with whichever DataSource you declare, in order to introduce whichever data in the list. So far so good.
Now, I'm building another class called OfferListView that extends ListView, and in order not to have an inneccessary extra file I wanted it to implement its own ListViewDataSource. The problem is that I can't call super(this, dimension) inside the constructor for this new class, as I'm then told that this can't be used before the superclass constructor has been called.
This "pattern" is what is used when programming with UIKit for iOS, and I think it's quite nice, but I can't get it to work in Java. How could I approach this?
Domain-View-Controller strategy was used in 90s on smaltalk to seperate view from domain and it is still being used in web-development.
Without writing all the classes for views etc, there are two ways for seperating view from domain.
(1st:) When view passes something to domain object then it keep polling to check for any additional changes. That means once a view object(a textfield, frame or anything else) has forwaded a request to domain it keeps checking after few seconds or minutes if something has changed. However this approach is not good.
(2nd:) The observer design pattern. When one thing changes it notifies automatically all listeners. Your view has to implement an interface and domain should provide a method for subscription for all objects which implement that interface. Here is an example and i did not compile it, However it clearly seperates view from domain.
public class View implements PropertyChangeListener {
private DomainObject object;
public View(DomainObject object) {
assert(object != null);
this.setObject(object);
}
public void enterText(String text) {
this.getObject().update(text);
}
#Override
public void propertyChange(PropertyChangeEvent evt) {
if(evt.getPropertyName().equals("string_updated"))
System.out.println("New value is " + evt.getNewValue());
}
public DomainObject getObject() {
return object;
}
public void setObject(DomainObject object) {
this.object = object;
}
}
Here is the domain class:
public class DomainObject {
private String text;
public DomainObject(String test) {
this.setText(test);
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public void update(String string) {
this.setText(string);
this.getListener().stream().forEach(e -> e.propertyChange(new PropertyChangeEvent(this,"string_updated","",this.getText())));
}
private ArrayList<PropertyChangeListener> listener;
public void subscribe(PropertyChangeListener listener) {
this.getListener().add(listener);
}
public ArrayList<PropertyChangeListener> getListener() {
return listener;
}
public void setListener(ArrayList<PropertyChangeListener> listener) {
this.listener = listener;
}
}
As i see, You are trying to have many views, if they are contained within eachother then use also Composite design pattern.

Refactoring a class implementing an interface by creating subclasses in GWT /java

I am implementing the frontend of an application in GWT (see attached picture) and I have view class which is getting bigger as more widgets are added to the frontend.
As stated in GWT tutorial, the view class must implement the Display interface of the presenter class. y problem is I have a lot a methods in that interface and as I implement them in the view class, it becomes too big. That's why I would like to refactor the code to reduce the size of the view class by implementing those methods in others
classes and reference them where needed in the view class;for instand by grouping them per group box (one class per group box).
Below is a sample code: Note that in the real application we have more widgets per group box.
The problem I am facing will be well explained as you read through the whole posting because I will be adding more details.
code to be refactored:
ContactPrewsenter.java
public class ContactPresenter {
public interface Display
{
void methodA();
void methodB();
void methodC();
void methodD();
void methodE();
void methodF();
.
.
.
void methodM();
}
public ContactPresenter()
{
//Some stuff here
}
......
......
#Override
public void bind(){
//Some stuff here
}
}
ContactView.java:
public class ContactView implements ContactPresenter.Display
{
private final Listbox listBoxA;
private final Listbox listBoxB;
private final Listbox listBoxC;
private final Listbox listBoxD;
private final Listbox listBoxE;
private final Listbox listBoxF;
private final Listbox listBoxG;
private final Listbox listBoxH;
private final Listbox listBoxI;
private final Listbox listBoxJ;
private final Listbox listBoxK;
private final Listbox listBoxL;
private final Listbox listBoxM;
public ContactView()
{
listBoxA = new ListBox();
listBoxB = new ListBox();
VerticalPanel vPanel1= new VerticalPanel();
vPanel1.add(listBoxA);
vPanel1.add(listBoxB);
GrooupBox groupBox1 = new GroupBox();
groupBox1.add(vPanel1);
listBoxC = new ListBox();
listBoxD = new ListBox();
VerticalPanel vPanel2 = new VerticalPanel();
vPanel2.add(listBoxC);
vPanel2.add(listBoxD);
GrooupBox groupBox2 = new GroupBox();
groupBox2.add(vPanel2);
listBoxE = new ListBox();
listBoxF = new ListBox();
VerticalPanel vPanel3 = new VerticalPanel();
vPanel3.add(listBoxE);
vPanel3.add(listBoxF);
GrooupBox groupBox3 = new GroupBox();
groupBox3.add(vPanel3);
listBoxE = new ListBox();
listBoxF = new ListBox();
VerticalPanel vPanel4 = new VerticalPanel();
vPanel4.add(ListBoxE);
vPanel4.add(ListBoxF);
....
GrooupBox groupBox3 = new GroupBox();
groupBox3.add(vPanel4);
listBoxG = new ListBox();
listBoxH = new ListBox();
....
VerticalPanel vPanel = new VerticalPanel();
vPanel.add(ListBoxG);
vPanel.add(ListBoxH);
....
GrooupBox groupBox4 = new GroupBox();
groupBox4.add(vPanel);
......
//create Horizontal/vertical panels, docklayout panel as well, to position the group boxes on the gui
....
}
#Override
void methodA(){
//uses listBoxA
}
#Override
void methodB(){
//used listBoxB
}
#Override
void methodC(){
//uses listBoxC
}
#Override
void methodD(){
//uses listBoxD
}
#Override
void methodE(){
//uses listBoxE
}
#Override
void methodF(){
//uses listBoxF
}
#Override
void methodG(){
//uses listBoxG
}
#Override
void methodH(){
//uses listBoxH
}
.
.
.
#Override
void methodM(){
//uses listBoxM
}
}
I have tried as follows:
ContactPreseter.java
public class ContactPresenter
{
public interface Display extends groupBox1View.Display, groupBox2View.Display, groupBox3View.Display, groupBox4View.Display
{
}
}
preseter classes of each group box
public class groupBox1Presenter
{
public interface Display
{
void methodA();
void methodB();
}
}
public class groupBox2Presenter
{
public interface Display
{
void methodC();
void methodD();
}
}
public class groupBox3Presenter
{
public interface Display
{
void methodE();
void methodF();
}
}
public class groupBox4Presenter
{
public interface Display
{
void methodG();
void methodH();
}
}
ContactView.java
public abstract class ContactView implements ContactPresenter.Display
{
// adds group boxes to horizontal/vertical panels, and docklayout panel
}
Below are the view classes for each group box:
But here I eclipse forces me to implement all the methods of the interface ContactPresenter.Display in each of these classes whereas , I wanted it to be the way you see implemented here.
I was wondering if there were a way to play with access modifiers in order to achieve that ? If not, please I would you to help with ideas how to do it ?
public groupBox1View extends ContactView implements groupBox1Presenter
{
public groupBox1View()
{
}
#Override
void methodA(){
//uses listBoxA
}
#Override
void methodB(){
//used listBoxB
}
}
public groupBox2View extends ContactView implements groupBox2Presenter
{
public groupBox2View()
{
}
#Override
void methodC(){
//uses listBoxC
}
#Override
void methodD(){
//used listBoxD
}
}
public groupBox3View extends ContactView implements groupBox3Presenter
{
public groupBox3View()
{
}
#Override
void methodE(){
//uses listBoxE
}
#Override
void methodF(){
//used listBoxF
}
}
public groupBox4View extends ContactView implements groupBox4Presenter
{
public groupBox4View()
{
}
#Override
void methodG(){
//uses listBoxG
}
#Override
void methodH(){
//used listBoxH
}
}
You are right, your view is growing too big. You need to cut it into components which are handling their own concerns.
The editor framework will prove helpful but has it's own caveats.
In the end, you have one presenter, working with the whole thing, but only reading and writing one contact object.
You build your view from multiple components, each may have it's own presenter and is responsible for one part of your large contact object.
An example: Instead of running 10 listboxes of generic type, make that 10 semantically different components, responsible for selection of different types of data: AgeListBox, CityListBox, FooListBox, BarListBox.
This will seperate the data provisioning for the boxes out of your central presenter, and into the specific presenters for each listbox.
Start at the lowest level and combine editing views for each semantic unit and combine them to larger return objects:
NameEditor, AgeEditor, FooEditor, BarEditor are combined into an AddressEditor, which assembles with a CVEditor into something bigger until you finally arrive at the contact level.
I hope this makes sense to you.
UPdate: You asked for code, let's try some pseudocode:
Let's say you have a profile you want to edit. It contains of
the user's personal data
contains the user address
a bunch of email or mobile addresses
an image or connection to Gravatar
payment information
the list of tags the user is interested in
the list of channels he subscribed
Newsletter/marketing information
public class UserProfile {
PersonalData person;
List<NewsTopic> topicsOfInterest;
List<NewsChannel> subscriptions;
MarketingInfo marketingInfo;
// bean stuff, constr, equals etc.
}
public class PersonalData {
String name;
String firstName;
List<ContactDevice>phoneAndMailList;
ImageContainer userImage;
BankAccount paymentData;
}
You get the idea, I guess...
You can now write ONE view class, detailing all the information you see here, resulting in a monolitic monster view and the matching monster presenter. Or you follow the advice in the gwtproject and cut the view in small as possible pieces (components). That is, subviews and presenters that form a hierarchy, matching the one of your UserProfile class. This is basically what the editor framework is really good at.
In the editor fw, the views are called "Editors" (makes sense), and they get fed the data from top editor down to the smallest part by an EditorDriver class. GWT will generate most of the code for you, which is very cool, but also is not working so perfect, if you have optional parts in the profile.
If we would implement this ourselves, you will build a structure like the following (I avoid the "Editor" and replaced by "Dialog"):
public class UserProfileDialogView extends Component implements HasValue<UserProfile> {
// subviews
#UiField
PersonalDataDialog personDataDlg;
#UiField
UserTopicListDialog topicListDlg;
#UiField
SubscriptionListDialog subscriptionListDlg;
#UiField
MarketingInfoDialog marketingInfoDlg;
#Overwrite
public UserProfile getValue() {
// we do not need to copy back the data from the sub-dialogs, because we gave them the values from the profile itself.
// Beware, substructures cannot be null in such a case!
return userProfile;
}
#Ovewrite
public void setValue(UserProfile profile) {
this.userProfile = profile;
// option one: manually distribute the profile parts
personDataDlg.getPresenter().setValue(profile.getPerson());
topicListDlg.getPresenter().setValue(profile.getTopicsOfInterest());
subscriptionListDlg.getPresenter().setValue(profile.getSubscriptions());
// option two: use eventbus and valuechanged event, dialogs are
}
}
There is now a variance of concerns: Who will set the value in the sub-dialogs. You can forward to the presenter of the sub-dialog, or you set it directly in the sub-dialog.
Anyway, what should get clear to you now, is that you do not have only one presenter to rule all parts of the view. You need to split the presenters to be responsible for one subview each. What I found useful in such a large dialog tree, was to have a separate model class, that keeps the object currently beeing edited and provides change communication logic for other dialogs. For example, if you add a list of topics, and you add one topic, the dialog for the channel subscription selection may want to know, that there is now one more topic to be shown in the topic-filter.

JavaFX custom dialog has references after removing froom scene

I have a custom dialog that is added to the scene and then removed again. Doing profiling with VisualVM, I noticed that even after a GC run the instance of this dialog is still retained.
I know that this means that there must be a reference to that object somewhere so I had a look at the references:
As seen in the image there are a lot of references from this$ which means inner classes, in this case they are bindings or ChangeListeners. The change listener can be replaced with WeakChangeListener. I'm not quite sure how I should handle the Bindings however.
Furthermore there are some references that do not make much sense at first glance:
bean of type SimpleStringProperty or SimpleObjectProperty
oldParent and value of type Node$1
So here are the concrete questions:
How to get around these strong references, so the object can actually be garbage collected? Would the use of lambda expressions instead of anonymous inner classes have any effect in this respect? How to figure out where the object is references by bean, oldParent and value.
EDIT1:
The bean references of type SimpleStringProperty are used in the super class and therefore should not cause an issue here, I guess. One SimpleObjectProperty bean reference comes from a utility method that provides an EventHandler. How would I resolve that, is there something similar for EventHandler as for ChangeListeners?
EDIT2:
I tried to come up with a simple application to reproduce the same thing. I could manage it and saw that I have basically the same fields listed in the heap dump, but then noticed that I have retained a reference to the component that is removed from the scene in my application. Once I let go of that reference it was cleaned up. The only noticeable difference is in my small example there is no reference in an Object array.
EDIT3:
I did some digging and found two places in the code that when commented out or not used, will not cause the object become eligible for garbage collection. The first one is this ChangeListener:
sailorState.numberOfSailorsProperty().addListener(new ChangeListener<Number>() {
#Override
public void changed(ObservableValue<? extends Number> observableValue,
Number oldValue, Number newValue) {
int inTavern = newValue.intValue()-sailorsAdditionalOnShip.get();
if (inTavern < 0) {
sailorsAdditionalOnShip.set(Math.max(sailorsAdditionalOnShip.get() + inTavern, 0));
inTavern = 0;
}
sailorsInTavern.set(inTavern);
}
});
The second one is a bit more complex. The component is a Dialog that has a close button. On pressing that one the dialog closes. This is the code of the button, I do not think that with this part is the problem, but for completeness sake:
public class OpenPatricianButton extends Control {
protected final StringProperty text;
protected final ReadOnlyObjectProperty<Font> currentFont;
protected final ObjectProperty<EventHandler<MouseEvent>> onAction;
public OpenPatricianButton(String text,
final Font font) {
super();
this.text = new SimpleStringProperty(this, "text", text);
this.currentFont = new ReadOnlyObjectPropertyBase<Font>() {
#Override
public Object getBean() {
return this;
}
#Override
public String getName() {
return "currentFont";
}
#Override
public Font get() {
return font;
}
};
this.onAction = new SimpleObjectProperty<EventHandler<MouseEvent>>(this, "onAction");
this.getStyleClass().add(this.getClass().getSimpleName());
}
#Override
public String getUserAgentStylesheet() {
URL cssURL = getClass().getResource("/ch/sahits/game/javafx/control/"+getClass().getSimpleName()+".css");
return cssURL.toExternalForm();
}
public StringProperty textProperty() {
return text;
}
public String getText() {
return text.get();
}
public void setText(String text) {
this.text.set(text);
}
public Font getFont() {
return currentFont.get();
}
public ObjectProperty<EventHandler<MouseEvent>> onActionProperty() {
return onAction;
}
public EventHandler<MouseEvent> getOnAction() {
return onAction.get();
}
public void setOnAction(EventHandler<MouseEvent> onAction) {
this.onAction.set(onAction);
}
}
public class OpenPatricianSmallWaxButton extends OpenPatricianButton {
public OpenPatricianSmallWaxButton(String text,
final Font font) {
super(text, font);
}
#Override
protected Skin<?> createDefaultSkin() {
return new OpenPatricianSmallWaxButtonSkin(this);
}
public OpenPatricianSmallWaxButton(String text) {
this(text, Font.getDefault());
}
}
public class OpenPatricianSmallWaxButtonSkin extends SkinBase<OpenPatricianSmallWaxButton> {
public OpenPatricianSmallWaxButtonSkin(final OpenPatricianSmallWaxButton button) {
super(button);
InputStream is = getClass().getResourceAsStream("sealingWaxFlattend.png");
Image img = new Image(is);
final ImageView imageView = new ImageView(img);
final Label label = new Label();
label.textProperty().bind(button.textProperty());
label.getStyleClass().add("OpenPatricianSmallWaxButtonLabeled");
label.setFont(button.getFont());
label.onMouseClickedProperty().bind(button.onActionProperty());
label.textProperty().bind(button.textProperty());
imageView.onMouseReleasedProperty().bind(button.onActionProperty());
StackPane stack = new StackPane();
stack.getChildren().addAll(imageView, label);
Group group = new Group(stack);
group.setManaged(false);
button.setPrefHeight(img.getHeight());
button.setPrefWidth(img.getWidth());
getChildren().add(group);
}
}
And here is the code fragment where the button is instantiated:
closeButton = new OpenPatricianSmallWaxButton("X", font);
closeButton.setLayoutX(WIDTH - CLOSE_BUTTON_WIDTH - CLOSE_BUTTON_PADDING);
closeButton.setLayoutY(CLOSE_BTN_Y_POS);
closeButton.setOnAction(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
executeOnCloseButtonClicked();
}
});
closeButton.getStyleClass().add("buttonLabel");
getContent().add(closeButton);
The call to remove the button is done through Guava AsyncEventBus. Therefore the code is a bit length. It starts in the Application thread and then gets posted to the event bus thread which then eventually has to call Platform.runLater:
protected void executeOnCloseButtonClicked() {
ViewChangeEvent event = new ViewChangeEvent(MainGameView.class, EViewChangeEvent.CLOSE_DIALOG);
clientEventBus.post(event);
}
public void handleViewChange(ViewChangeEvent event) {
if (event.getAddresse().equals(MainGameView.class)) {
if (event.getEventNotice() instanceof DialogTemplate) {
setNewDialog((DialogTemplate) event.getEventNotice());
} else {
sceneEventHandlerFactory.getSceneEventHandler().handleEvent(event.getEventNotice());
}
}
}
public void handleEvent(Object eventNotice) {
Preconditions.checkNotNull(dialogContoller, "Dialog controller must be initialized first");
if (eventNotice == EViewChangeEvent.CLOSE_DIALOG) {
dialogContoller.closeDialog();
}
....
public void closeDialog() {
if (Platform.isFxApplicationThread()) {
closeDialogUnwrapped();
} else {
Platform.runLater(() -> closeDialogUnwrapped());
}
}
private void closeDialogUnwrapped() {
if (dialog != null) {
new Exception("Close dialog").printStackTrace();
getChildren().remove(dialog);
dialog = null;
dialogScope.closeScope();
}
}
The really peculiar thing is that the dialog can be cleaned up by the GC (provided the first issue with the ChangeListener is commented out) when I call closeDialog from a timer. In other words this behaviour does only happen if I close the dialog with a mouse click.

Swing Popup menus are not completely painted

I have this in several areas of an app I'm working on and I can see no way to replicate it outside of this app. I can't create a sscce since I can't manage to replicate this at all - This leads me to believe that it must be something caused by the parent frame / app, but I have no idea where to look.
What I see is that part of the left hand side of popup menus are not painted. I see this behaviour with JCombobox popups as well as JPopupMenu's. I've attached a couple of images to show what I mean. most of these did work properly previously and without any changes to the code where the popupmenu's are created or displayed, this problem has spread to a lot of other places now.
I'm not mixing heavyweight and lightweight components, as we only use Swing components and the two examples I show below are in completely different parts of the app. The first one is in a fairly simple panel with very little functionality, but the second example (JPoopupMenu) is in a very complex legacy panel.
On both of these and other place where I see it, I'm not altering the parent's clipping region at all and in all case, these popups are constructed and displayed on the EDT.
I know this question is rather vague, but that is because of the nature of the problem. I'll provide any requested info.
This specific case happens to be a custom combobox model, but we've seen it when using the DefaultComboBoxModel as well:
public class GroupListModel extends AbstractListModel
implements ComboBoxModel{
private List<groupObject> groups;
private groupObject selectedItem = null;
public GroupListModel() {
this(new ArrayList<groupObject>());
}
public GroupListModel(List<groupObject> groups) {
this.groups = groups;
}
#Override
public int getSize() {
return groups.size();
}
#Override
public Object getElementAt(int index) {
if(index>=groups.size()){
throw new IndexOutOfBoundsException();
}
return groups.get(index);
}
public void setGroups(List<groupObject> groups){
this.groups = groups;
fireContentsChanged(this, 0, groups.size());
}
public void addElement(groupObject group){
groups.add(group);
fireIntervalAdded(this, groups.size()-1, groups.size()-1);
}
public void addElement(groupObject group, int index){
groups.add(index, group);
fireIntervalAdded(this, index, index+1);
}
#Override
public void setSelectedItem(Object anItem) {
if(anItem instanceof groupObject){
selectedItem = (groupObject) anItem;
}else{
throw new IllegalArgumentException();
}
fireContentsChanged(this, 0, groups.size());
}
#Override
public Object getSelectedItem() {
return selectedItem;
}
This is a JPopupMenu that gets displayed when you right click using the following code:
public void mouseClicked(MouseEvent e) {
if( e.getButton()==e.BUTTON3 ){
lastClickedID = tmp.getUniqueID();
lastClickedGui = (bigEventGui) gui;
itmComplete.setText(
completed ?
ctOne.getLang("uncomplete") :
ctOne.getLang("complete") );
itmComplete.setIcon( (completed ?
iconFramework.getIcon(
iconFramework.UNCOMPLETE_ITEM,
24, false) :
iconFramework.getIcon(
iconFramework.COMPLETE_ITEM,
24, false) ));
popRCEvent.show(gui, e.getX(), e.getY() );
}
Taking out JPopupMenu.setDefaultLightWeightPopupEnabled(false); fixed it... Can somebody please try and explain why?

How to add onclick selection to rows of wicket TreeTable?

I'm working with a TreeTable (from wicket-extensions) and I'd like to be able to select a row by clicking anywhere within it instead of the usual behavior of clicking the link in one cell to select the row. I understand this should be possible by adding an AjaxEventBehavior("onclick") to the component representing the row, but I can't seem to find any methods where the row component is exposed.
I figured out a solution after. The row element is available in the populateTreeItem method from TreeTable. When you're creating your treetable, override this method like so:
#Override
protected void populateTreeItem(final WebMarkupContainer item, final int level) {
super.populateTreeItem(item, level);
item.add(new AjaxEventBehavior("onclick") {
#Override
protected void onEvent(final AjaxRequestTarget target) {
final TreeNode node = ((TreeNode) item.getDefaultModelObject());
rowClickSelect(node);
});
}
};
Generally useful in adding behaviors to rows. In my case, I'll have to do some more overriding to reconcile this toggle-on-click behavior with the clicks that are supposed to expand/contract nodes as well as link clicks.
Just toggling selection again in these cases has the unfortunate effect of briefly toggling the node in and out of the unwanted state, which is not ideal. Instead, override the onJunctionLinkClicked and onNodeLinkClicked methods, which will be touched by a click event before it gets to the onClick behavior we just set-up in populateTreeItem:
#Override
protected void onJunctionLinkClicked(final AjaxRequestTarget target, final TreeNode node) {
super.onJunctionLinkClicked(target, node);
skipNextRowClick();
}
#Override
protected void onNodeLinkClicked(final AjaxRequestTarget target, final TreeNode node) {
super.onNodeLinkClicked(target, node);
skipNextRowClick();
}
Finally, add the methods skipNextRowClick and rowClickSelect:
/**
* Ensure the next call to rowClickSelect() will have no effect.
*/
private void skipNextRowClick() {
this.skipNextClickSelect = true;
}
private void rowClickSelect(final TreeNode node) {
if (this.skipNextClickSelect) {
this.skipNextClickSelect = false;
return;
}
// select on click row
final boolean isSelected = Log4jPanel.this.treeTable.getTreeState().isNodeSelected(node);
treeTable.getTreeState().selectNode(node, !isSelected);
}

Categories