I Have an odd problem - not sure if there's a coding mistake or a bug in CN1.
Basically I create a row of CheckBox objects and put them in a container that is X-Scrollable. If i click on one un-selected item and drag until the "elastic" effect pulls it back, it appears to be selected, but the code does not record it as selected.
Please see the following video of the issue:
https://youtu.be/EtputE1kjyo
Note that in the Console output, the word 'selected' is capitalized when the field has been selected and lowercase when it is unselected. Same for focus (I added focus to the output to determine if setFocusable() was working as desired so that focus was not to blame for the selection error).
here's the Checkbox creation code:
cb = new CheckBox(getCacheableImageMaxHeight(mod.getIconFile(),moduleImageHeight));
cb.setName(mod.getModuleID());
cb.setToggle(true);
cb.setUIID("ModuleButton");
cb.setFocusable(false);
cb.setScrollVisible(false);
cb.setTextPosition(Component.BOTTOM);
cb.setCloudDestinationProperty(cb.getName());
//actionlistener added for debugging only
final CheckBox cbFinal = cb;
final String modName = mod.getDisplayName();
cb.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent evt) {
System.out.println(modName+", "+(cbFinal.isSelected()?"SELECTED":"selected") + ", " + (cbFinal.hasFocus()?"FOCUS":"focus"));
}
});
c.addComponent(cb);
UPDATE: I've realized there are two "states" at war here:
The toggleButtons (I now realize they're not just CheckBoxes since I set "setToggle(true)) are getting stuck in the "pressed" state as they are dragged and released with the "elastic" effect. Unfortunately, the "pressed" and "selected" states have the same appearance so that means my users think they have selected something when it's just stuck being "pressed" during a drag operation.
Here's some more debugging I did.
The first button is Pressed, but not selected (the bug).
the second button is Selected normally and not showing the bug.
The Third button is interesting because I selected it, then dragged and released it to get it to be SELECTED and PRESSED!
So the question changes to: Is there an open bug for this situation already (Pressed state gets stuck on after button is released) and if so, is there a fix coming or a workaround for now?
Just style the selected state to look different from the pressed state and it should work fine.
In a touch device selected state isn't rendered when the finger is up. This is almost always true unless you changed a flag in Display or set some arcane theme constant.
So I figured out a more effective workaround that doesn't involve adding a separate pressed style (since there could be buttons selected, pressed, and selected+pressed with the bug)
I needed to capture the event that scrolling stopped and then check the state of the buttons to make sure none were still pressed. To do this, I used addPointerReleasedListener on the scrolling container to detect when the pointer came off (so its components are definitely no longer pressed), and then in its Runnable, I make sure each one is released.
scrollingContainer.addPointerReleasedListener(evt -> {
Container cont = (Container) evt.getComponent();
Iterator<Component> buttons = cont.iterator();
while (buttons.hasNext()){
Button button = (Button) buttons.next();
if (button.getState() == Button.STATE_PRESSED) {
button.released();
}
}
});
So far seems to solve the problem. Now we just need a permanent fix, or a note in the documentation of ToggleButtons that when they are in a scrolling container, they could get stuck in a pressed state and need to be released.
Related
I am creating a basic terminal chat application in Java using Lanterna. I have a TextBox component that I call addLine() on as new messages come in. The default behavior of a TextBox appears to be to maintain its previous scroll position until the user focuses on it and scrolls manually. I would like for the TextBox itself to scroll to the bottom automatically.
There is no obvious way to set the scroll position of a TextBox programmatically, so my best idea was to extend TextBox and make my own version of addLine():
import com.googlecode.lanterna.TerminalSize;
import com.googlecode.lanterna.gui2.TextBox;
public class ChatWindowTextBox extends TextBox{
public ChatWindowTextBox(TerminalSize preferredSize, Style style){
super(preferredSize, style);
}
public ChatWindowTextBox addLineAndScrollDown(String line){
addLine(line);
setReadOnly(false); // I make the chat window read only
// in my screen setup, so I undo that
takeFocus();
setCaretPosition(Integer.MAX_VALUE, Integer.MAX_VALUE);
setReadOnly(true);
return this;
}
}
Via debugging, I have verified that the arguments to setCaretPosition get correctly clamped to the actual values of the last line and column, and the internal value of caretPosition is updated to those values. However, this does not make the TextBox scroll. Have I misunderstood how setCaretPosition() behaves, and is there a viable way to programmatically make a TextBox scroll to the bottom?
A solution I found is to force the program to pause before making the ChatWindowTextBox read-only again:
public ChatWindowTextBox addLineAndScrollDown(String line){
super.addLine(line);
setReadOnly(false);
takeFocus();
setCaretPosition(Integer.MAX_VALUE, Integer.MAX_VALUE);
try {
Thread.sleep(15);
} catch (InterruptedException e) {
e.printStackTrace();
}
setReadOnly(true);
return this;
}
15 ms is about the smallest pause that allows the ChatWindowTextBox to scroll as expected. You may want to go for 20 or 30 ms.
The primary problem with the original code has to do with making the ChatWindowTextBox read-only. The solution proposed in the question actually works perfectly for TextBoxes that are not made read-only.
Digging into Lanterna's source code, the caret position of a TextBox is not taken into account if it was set to read-only. However, the code in the question would appear to account for this by unsetting readOnly, changing the caret position, then resetting readOnly. So why is there unexpected behavior? Internally, readOnly() also calls invalidate(), which according to Lanterna, "Marks the component as invalid and requiring to be drawn at next opportunity." I assume that the ChatWindowTextBox being invalidated and soon redrawn causes the change in caret position to not completely go through before it is made read-only again.
I try to do an image creator program in java (with squares/circles/etc)
I have a few JRadioButtons in a ButtonGroup that symbolizes my program's "mode" (if I draw a circle, something else/if I move the objects).
When I click on different modes, the "mode" changes and I'm able to do what I want.
My problem is when I try to change the mode by double-clicking on an object. I do it in a MouseListener. I'm able to select the object, to change the "mode", but I can't change the selected JRadio Button on my ButtonGroup.
I searched for a while (since the setSelected() is not working). I know that ButtonGroup can have only a button selected at once. How could I deselect the curent one and select the one I need (the first one).
Thank you for any advices.
From the docs:
public void setSelected(boolean b)
Sets the state of the button. Note that this method does not trigger
an actionEvent. Call doClick to perform a programatic action change.
As mentioned here use:
radioBtn.doClick();
I created a small method that allow me to set any radio group button. Very convenient if you don't want to use if for any radio button.
public void setButtonGroup(int rdValue, Enumeration elements ){
while (elements.hasMoreElements()){
AbstractButton button = (AbstractButton)elements.nextElement();
if(Integer.parseInt(button.getActionCommand())==rdValue){
button.setSelected(true);
}
}
}
then
setButtonGroup(yourValue, yourButtonGroup.getElements());
While trying to learn JButton events I'm getting confused because of a problem. The sample code I'm using is here.
The code is below:
jb.addChangeListener(new ChangeListener()
{
#Override
public void stateChanged(ChangeEvent e)
{
System.out.println("Changed");
}
});
This is a demo application so only one button is used and it has the focus when the application is starting up. I simply press the spacebar to simulate a click event. As per the tutorial (by OReilly: Java Swing) it should fire the change event twice followed by an action event followed by action event. i.e.
Changed
Changed
ActionEvent
Changed
But after the 4th event I got another ChangeEvent. There is actually 5 events for a single click in the way mentioned above. If I'm trying to click with mouse the result is even more different. When the mouse entered into the button region, an event fired.
I don't know what change occurs and ChangeEvent is raised when the mouse hovers over the JButton. I don't find a similar method like getChangedState (ItemStageChanged) for JButton to know what state changed in that button. Since it's about learning, I don't want to use ActionListener unless I understand this issue.
So my questions are:
Why do I see the ChangeEvent twice where it should be one?
How do I find what state is changed in JButton?
Edit:
I see there are 5 states mentioned in DefaultButtonModel and they are defined in ButtonModel interface. But JButton don't have fields,methods to get those states. did they get ignored willingly? Or JButton(and AbstractButton) don't relates with ButtonModel interface.
Edit 2:
The tutorial indicates,
Depending on the L&F, there may also be additional ChangeEvents.
I'm using Swing's default L&F in Windows but I'm getting different results than tutorial's expectation.
The five events that happen when you press the button, in order are:
ChangeEvent: Armed - true
ChangeEvent: Pressed - true
ActionEvent
ChangeEvent: Pressed - false
ChangeEvent: Armed - false
If you use your mouse to press the button then in addition to the above,
there will be a ChangeEvent for Rollover: true when the mouse moves over the button and another for Rollever: false when the mouse moves away from the button.
How, using JTextField, can I create my program to enable/disable a textfield depending on whether or not a checkbox is ticked?
I have an option which, if checked, needs to take input. If not checked, I'd like the text field to remain grayed out with the user unable to enter text.
mchq08 did not give a complete answer since his code will do nothing if the JCheckBox is unchecked. You don't need the if block as all you'd need is a single line of code in your item listener
checkBox.addItemListener(new ItemListener() {
public void itemStateChanged(ItemEvent itemEvent){
// the line below is the line that matters, that enables/disables the text field
textField.setEnabled(itemEvent.getStateChange() == ItemEvent.SELECTED);
}
});
You can do that task with the check box with a simple if/else statement within an event listener.
You would want to put this into an event Listener for the check Box so when the event is fired it will allow it to editable. You could also go a step more and create another if/else if the check box is false and delete the text within the text box and set it to editable.
if(checkBox.isEnabled()){
textBox.setEditable(true);
}
Link CheckBox
Link TextBox
I have a program which prompts users to select a choice out of four options (from a group of RadioButtons).
Once the user has made a choice, he/she clicks a button and then receives a message. After closing the window, the user will go back to the first window and make a new selection if desired. What I want is for the radiobuttons to be totally clear.
So far I have implemented a method to actually unselect the radiobuttons and works well (clears the values of the variables), what it doesn't do is to remove the black spot from the previously selected radiobutton. In the other hand this same method works fine with unselecting and unchecking checkboxes.
Any tip to fix this little issue?
Here's my code:
public void clean() {
jRadioButton1.setSelected(false);
jRadioButton2.setSelected(false);
jRadioButton3.setSelected(false);
jRadioButton4.setSelected(false);
jCheckBox1.setSelected(false);
jCheckBox2.setSelected(false);
}
make them group of buttons and then buttonGroup1.clearSelection();