Checkbox state propagating to all instances of URCap

Hello Guys,

I’m working on my first URCap and I encountered a weird behavior of JCheckBox control. It changes it’s visibility (checked / not checked) on all instances of my URCap in the program at once. Only the visibility is changed without firing a event.
I created a sample project to test it and below you will find a code used to that:
View

JCheckBox checkBox = new JCheckBox();	
	ViewAPIProvider apiProvider;
	
	JLabel label = new JLabel();
	
	public WaypointTestProgramNodeView(ViewAPIProvider apiProvider) {
	this.apiProvider = apiProvider;
	}
	@Override
	public void buildUI(JPanel panel, final ContributionProvider<WaypointTestProgramNodeContribution> provider) {
		panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
		
		checkBox.setText("Check box");
		checkBox.setPreferredSize(new Dimension(150,30));
		checkBox.setMaximumSize(checkBox.getPreferredSize());
		
		checkBox.addItemListener(new ItemListener() {
			
			@Override
			public void itemStateChanged(ItemEvent e) {				
				if (e.getStateChange() == ItemEvent.SELECTED){
					provider.get().onCheckboxStateChanges(true);
				}
				else {
					provider.get().onCheckboxStateChanges(false);
				}
			}
		});
		
		panel.add(checkBox);

		label.setPreferredSize(new Dimension(150,30));
		label.setMaximumSize(label.getPreferredSize());
		panel.add(label);	
		
	}
	public void setLabel(String value) {
		label.setText(value);
	}

Contribution:

private Boolean getCheckbox()
	{
		return dataModel.get(CHECKBOX_KEY, false);
		
	}
	private void setState(Boolean state)
	{
		if (state)
			view.setLabel("Checked");
		else
			view.setLabel("Not checked");
	}
	public void onCheckboxStateChanges(final Boolean state)
	{
		apiProvider.getProgramAPI().getUndoRedoManager().recordChanges(new UndoableChanges() {
			
			@Override
			public void executeChanges() {
				dataModel.set(CHECKBOX_KEY, state);
				setState(state);
			}
		});
	}

As a new user I cannot add any attachments but I’m open to provide you with screen recording as well as source code of my sample program.

Could you please suggest me what I did wrongly here?

Thanks,
Michał Besler

Usually when its a visibility thing, you’re probably missing it in your openView() method in the contribution. You should be setting the state of the checkbox there based on the output of some model.get() function.

Hello Eric,
Thank you very much for your reply. Unfortunately that’s not the case here. In openView I already had setting the state.
Below it full source code of my program:


public class WaypointTestProgramNodeContribution<EllipseProgramNodeView> implements ProgramNodeContribution{

	
	private static final String CHECKBOX_KEY = "checkbox";	
	
	private final ProgramAPIProvider apiProvider;
	private final DataModel dataModel;
	private final ProgramNodeFactory programNodeFactory;
	private final WaypointTestProgramNodeView view;

	
	public WaypointTestProgramNodeContribution(ProgramAPIProvider apiProvider, WaypointTestProgramNodeView view, DataModel model) {
	this.apiProvider = apiProvider;
	this.dataModel = model;
	this.view = view;
	
	programNodeFactory = apiProvider.getProgramAPI().getProgramModel().getProgramNodeFactory();
	
	}

	@Override
	public void openView() {
		setState(getCheckbox());
		
	}

	@Override
	public void closeView() {
		// TODO Auto-generated method stub
		
	}

	@Override
	public String getTitle() {
		return	"WaypointTest";
	}

	@Override
	public boolean isDefined() {
		// TODO Auto-generated method stub
		return true;
	}

	@Override
	public void generateScript(ScriptWriter writer) {
		
		
		
	}
	private Boolean getCheckbox()
	{
		return dataModel.get(CHECKBOX_KEY, false);
		
	}
	private void setState(Boolean state)
	{
		if (state)
			view.setLabel("Checked");
		else
			view.setLabel("Not checked");
	}
	public void onCheckboxStateChanges(final Boolean state)
	{
		apiProvider.getProgramAPI().getUndoRedoManager().recordChanges(new UndoableChanges() {
			
			@Override
			public void executeChanges() {
				dataModel.set(CHECKBOX_KEY, state);
				setState(state);
			}
		});
	}
	
}

Well I see where you’re setting the JLabel, but not the state of the checkbox. You should add a “checkbox.setSelected(state)” inside the method that you’re setting the label.

1 Like

Hello Eric,
Thanks for the follow up. Indeed adding that fixed problem with my program. Thanks a lot for your help!

In case somebody had similar issues in the future I’m adding code of the corrected version of my program below:

View:

public class WaypointTestProgramNodeView implements SwingProgramNodeView<WaypointTestProgramNodeContribution> {

	JCheckBox checkBox = new JCheckBox();	
	ViewAPIProvider apiProvider;
	
	JLabel label = new JLabel();
	
	public WaypointTestProgramNodeView(ViewAPIProvider apiProvider) {
	this.apiProvider = apiProvider;
	}
	@Override
	public void buildUI(JPanel panel, final ContributionProvider<WaypointTestProgramNodeContribution> provider) {
		panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
		
		checkBox.setText("Check box");
		checkBox.setPreferredSize(new Dimension(150,30));
		checkBox.setMaximumSize(checkBox.getPreferredSize());		
		
		checkBox.addItemListener(new ItemListener() {			
			@Override
			public void itemStateChanged(ItemEvent e) {				
				if (e.getStateChange() == ItemEvent.SELECTED){
					provider.get().onCheckboxStateChanges(true);
				}
				else {
					provider.get().onCheckboxStateChanges(false);
				}
			}
		});		
		panel.add(checkBox);
		label.setPreferredSize(new Dimension(150,30));
		label.setMaximumSize(label.getPreferredSize());
		panel.add(label);			
	}
	public void setLabel(String value) {
		label.setText(value);
	}
	public void setCheckbox(Boolean value) {
		checkBox.setSelected(value);
	}
}

Contribution:

public class WaypointTestProgramNodeContribution<EllipseProgramNodeView> implements ProgramNodeContribution{

	
	private static final String CHECKBOX_KEY = "checkbox";	
	
	private final ProgramAPIProvider apiProvider;
	private final DataModel dataModel;
	private final ProgramNodeFactory programNodeFactory;
	private final WaypointTestProgramNodeView view;

	
	public WaypointTestProgramNodeContribution(ProgramAPIProvider apiProvider, WaypointTestProgramNodeView view, DataModel model) {
	this.apiProvider = apiProvider;
	this.dataModel = model;
	this.view = view;
	
	programNodeFactory = apiProvider.getProgramAPI().getProgramModel().getProgramNodeFactory();	
	}

	@Override
	public void openView() {
		setState(getCheckbox());		
	}

	@Override
	public void closeView() {
		// TODO Auto-generated method stub
		
	}

	@Override
	public String getTitle() {
		return	"WaypointTest";
	}

	@Override
	public boolean isDefined() {
		// TODO Auto-generated method stub
		return true;
	}

	@Override
	public void generateScript(ScriptWriter writer) {		
	}
	private Boolean getCheckbox()
	{
		return dataModel.get(CHECKBOX_KEY, false);		
	}
	private void setState(Boolean state)
	{
		if (state)
		{
			view.setLabel("Checked");
			view.setCheckbox(true);
		}	
		else
		{
			view.setLabel("Not checked");
			view.setCheckbox(false);
		}
	}
	public void onCheckboxStateChanges(final Boolean state)
	{
		apiProvider.getProgramAPI().getUndoRedoManager().recordChanges(new UndoableChanges() {
			
			@Override
			public void executeChanges() {
				dataModel.set(CHECKBOX_KEY, state);
				setState(state);
			}
		});
	}
}

this concept tripped us up in the beginning of learning urcap dev, too!

There is only ever ONE single view instance of a program node, regardless of how many instances of the program node there are in the program.

…so… you need to:

  1. store the state/value in the model of that program node instance
  2. update the visuals in openView by referencing the stored model value