I have a created a class of ComboBox popup menu listener to change the width of popup menu from the actual width of combobox.
protected void customizePopup(BasicComboPopup popup)
{
scrollPane = getScrollPane(popup);
if (popupWider)
popupWider( popup );
checkHorizontalScrollBar( popup );
// For some reason in JDK7 the popup will not display at its preferred
// width unless its location has been changed from its default
// (ie. for normal "pop down" shift the popup and reset)
Component comboBox = popup.getInvoker();
Point location = comboBox.getLocationOnScreen();
if (popupAbove)
{
int height = popup.getPreferredSize().height;
popup.setLocation(location.x, location.y - height);
}
else
{
int height = comboBox.getPreferredSize().height;
popup.setLocation(location.x, location.y + height - 1);
popup.setLocation(location.x, location.y + height);
}
}
And this method is called from
public void popupMenuWillBecomeVisible(PopupMenuEvent e)
{
JComboBox comboBox = (JComboBox)e.getSource();
if (comboBox.getItemCount() == 0) return;
final Object child = comboBox.getAccessibleContext().getAccessibleChild(0);
if (child instanceof BasicComboPopup)
{
SwingUtilities.invokeLater(new Runnable()
{
public void run() {
customizePopup((BasicComboPopup)child);
}
});
}
}
calling method CustomizePopup() outside EDT causes NullPointerException.
Can anyone tell me what is the reason??
Why do we need another EDT to process CustomizePopup()?
public void popupMenuWillBecomeVisible(PopupMenuEvent e)
{
JComboBox comboBox = (JComboBox)e.getSource();
if (comboBox.getItemCount() == 0) return;
final Object child = comboBox.getAccessibleContext().getAccessibleChild(0);
if (child instanceof BasicComboPopup)
{
customizePopup((BasicComboPopup)child);
}
}
Related
In an attempt to fix a problem with printing within the margins, I'm trying to scale my forms so that they'd shrink to the size of the paper it will be printed to.
Inside Printable.java that extends VBox
public void scaleToFit(){
double maxWidth = 497.0;
this.requestLayout();
double width = this.getWidth();
if(width > maxWidth){
double widthFrac = maxWidth / width;
this.setScaleX(widthFrac);
}
System.out.println(this.getWidth());
//edited out same process for height
}
Printable will be kept in a HashMap data.table. When my printing window is loaded I run scaleToFit for each of them. main is a ScrollPane.
Inside ModPrintCycle.java that extends VBox
//inside constructor
main.sceneProperty().addListener(new ChangeListener<Scene>(){
#Override
public void changed(ObservableValue<? extends Scene> observable, Scene oldValue, Scene newValue) {
System.out.println("new Scene");
newValue.windowProperty().addListener(new ChangeListener<Window>(){
public void changed(ObservableValue<? extends Window> arg0, Window arg1, Window arg2) {
System.out.println("new Window");
arg2.setOnShown(new EventHandler<WindowEvent>(){
#Override
public void handle(WindowEvent event) {
Platform.runLater(new Runnable(){
#Override
public void run() {
System.out.println(event.toString());
lookingAt = data.tables.size();
while(lookingAt-1 >= 0 ){
showNext(-1);
}
}
});
}
});
}
});
}
});
Just for this example, I also added scaleToFit() in the button that changes between these Printables. [EDIT: Added the scripts that explicitly show the use of scaleToFit()] Note that data.tables is a HashMap containing the Printables.
Inside ModPrintCycle.java continuation
private void showNext(int move){
boolean k = false;
if(move > 0 && lookingAt+move < data.tables.size()){
k = true;
}
else if(move < 0 && lookingAt+move >=0){
k = true;
}
if(k){
lookingAt+= move;
}
show();
}
private void show(){
if(data.tables.size() > 0){
if(lookingAt >= 0 && lookingAt < data.tables.size()){
//tableOrder is an ArrayList<String> for the arrangement of data.tables
if(tableOrder.size() > 0){
Printable pt = data.tables.get(tableOrder.get(lookingAt));
main.setContent(pt);
pt.scaleToFit();
}
}
else{
if(lookingAt < 0){
lookingAt = 0;
show();
}
else if(lookingAt >= data.tables.size()){
lookingAt = data.tables.size()-1;
show();
}
}
txtStatus.setText((lookingAt+1) + " / " + data.tables.size());
}else{
main.setContent(null);
txtStatus.setText("Empty");
}
}
public void printAll(ArrayList<String> pageList){
//PrinterJob pj is global
//conditions and try declarations
pj = PrinterJob.createPrinterJob(curP);
PageLayout pp = curP.createPageLayout(Paper.LEGAL, PageOrientation.PORTRAIT, MarginType.DEFAULT);
PageLayout pl = curP.createPageLayout(Paper.LEGAL, PageOrientation.LANDSCAPE, MarginType.DEFAULT);
for(String p : pageList){
Printable pt = data.tables.get(p);
pt.scaleToFit();
if(pt.isLandscape()){
pj.printPage(pl,pt);
}
else{
pj.printPage(pp,pt);
}
}
pj.endJob();
// catch statements
}
However, whenever scaleToFit() is called for the first time (when ModPrintCycle is loaded), it tells me that the widths are 0, thus will not scale yet. The second time it is called (when I change between them for the first time), it's still 0. When it finally runs a 3rd time (when I look back at the Printable), it finally works and changes the widths as needed.
Since these Printables need to be printed, I cannot ensure that the forms are scaled until someone looks through all of them twice.
How do I force the forms to take their bounds before having to load them?
Since the code you posted is not fully executable (i.e. not MCVE or SSCCE), the problem cannot be reproduced. It is also difficult to guess the cause. But I see your purpose, so I suggest instead of scaling it manually, let the printable scale itself automatically through listener:
#Override
public void start( Stage stage )
{
ScrollPane scrollPane = new ScrollPane( new Printable( new Label( "looong loooong loooooong looooong loooong text" ) ) );
stage.setScene( new Scene( scrollPane ) );
stage.show();
}
class Printable extends VBox
{
public Printable( Node... children )
{
super( children );
Printable me = this;
this.widthProperty().addListener( new ChangeListener<Number>()
{
#Override
public void changed( ObservableValue<? extends Number> observable, Number oldValue, Number newValue )
{
double maxWidth = 100.0;
double width = newValue.doubleValue();
if ( width > maxWidth )
{
double widthFrac = maxWidth / width;
me.setScaleX( widthFrac );
}
}
} );
}
}
I currently have an application which will create a textarea wherever the user clicks. However, I want the pane to only be editable when a certain condition is true. The clickable area never goes away, though. How can I change this so the area is only clickable if myAnchorPane.isVisible() is true?
double oldHeight = 0;
double oldWidth = 0;
#FXML
private void handleTextButton() {
System.out.println("Text Clicked");
TextHeaderTools.setVisible(false);
BackgroundTools.setVisible(false);
VideoTools.setVisible(false);
PageTitleTools.setVisible(false);
TemplateTools.setVisible(false);
ImageTools.setVisible(false);
TextTools.setVisible(true);
workspace.addEventHandler(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent me) {
int x = (int) me.getX();
int y = (int) me.getY();
System.out.println("(" + x +", " + y +")");
InlineCssTextArea newArea = new InlineCssTextArea();
newArea.relocate(x, y);
newArea.setStyle("-fx-background-color: transparent;");
Text textHolder = new Text();
newArea.setPrefSize(40,40);
textHolder.textProperty().bind(newArea.textProperty());
textHolder.layoutBoundsProperty().addListener(new ChangeListener<Bounds>() {
#Override
public void changed(ObservableValue<? extends Bounds> observable, Bounds oldValue, Bounds newValue) {
if (oldHeight != newValue.getHeight()) {
oldHeight = newValue.getHeight();
newArea.setPrefHeight(textHolder.getLayoutBounds().getHeight() + 30);
}
if (oldWidth != newValue.getWidth()){
oldWidth = newValue.getWidth();
newArea.setPrefWidth(textHolder.getLayoutBounds().getWidth() + 30);
}
}
});
workspace.getChildren().addAll(newArea);
} //end handle
});
}
EDIT:
The condition is
myAnchorPane.isVisible()
You can achieve this nicely with ReactFX.
Instead of workspace.addEventHandler(MOUSE_CLICKED, ...), do this:
EventStreams.eventsOf(workspace, MOUSE_CLICKED)
.suppressWhen(myAnchorPane.visibleProperty().not())
.subscribe((MouseEvent me) -> {
// handle mouse click
});
I see you use RichTextFX, so you already have ReactFX as a dependency anyway.
I use this code fragment to perform some action on a tree when the mouse is double-clicked: open a window and get the node which was double clicked by the mouse, but it doesn't return anything, it returns null:
MouseListener ml = new MouseAdapter() {
public void mousePressed(MouseEvent e) {
int selRow = contactTree.getRowForLocation(e.getX(), e.getY());
TreePath selPath = contactTree.getPathForLocation(e.getX(), e.getY());
System.out.println(contactTree.getEditingPath());
Account memberToChat;
if(selRow != -1) {
if(e.getClickCount() == 1) {
}
else if(e.getClickCount() == 2) {
new ChatWindow().setVisible(true);
memberToChat=(Account)node.getUserObject(); // node is declared somewhere in the class as DefaultMutableTreeNode node
System.out.println(memberToChat.getFirstName()+" "+memberToChat.getEmail());
}
}
}
};
for JTree to set proper setSelectionMode
add TreeSelectionListener
example with TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION
In GUI,I am displaying one JTree at the left hand side of JPanel. Now for each Node(leaf), on Mouse right click I want to display JPopup menu asking for displaying the statistics about Node in right JPanel.
As i am new to swing,Could any one help with code.
Thanks in Advance.
Regards,
Tushar Dodia.
Use JTree's method
public TreePath getPathForLocation(int x, int y)
Then TreePath
public Object getLastPathComponent()
That returns you desired node from point where user right clicked.
Seem to have caused a bit of confusion (confusing myself ;-) - so here's a code snippet for doing target location related configuration of the componentPopup
JPopupMenu popup = new JPopupMenu();
final Action action = new AbstractAction("empty") {
#Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
}
};
popup.add(action);
JTree tree = new JTree() {
/**
* #inherited <p>
*/
#Override
public Point getPopupLocation(MouseEvent e) {
if (e != null) {
TreePath path = getClosestPathForLocation(e.getX(), e.getY());
action.putValue(Action.NAME, String.valueOf(path.getLastPathComponent()));
return e.getPoint();
}
action.putValue(Action.NAME, "no mouse");
return null;
}
};
tree.setComponentPopupMenu(popup);
I took #kleopatra solution and changed it slightly.
Maybe it isn't the best way but works for me.
JTree tree = new JTree() {
private static final long serialVersionUID = 1L;
#Override public Point getPopupLocation(MouseEvent e) {
if (e == null) return new Point(0,0);
TreePath path = getClosestPathForLocation(e.getX(), e.getY());
Object selected = path != null ? path.getLastPathComponent() : null;
setComponentPopupMenu(getMenuForTreeNode(getComponentPopupMenu(), selected));
setSelectionPath(path);
return e.getPoint();
}
};
public JPopupMenu getMenuForTreeNode(JPopupMenu menu, Object treeNode) {
if (menu == null) menu = new JPopupMenu("Menu:");
menu.removeAll();
if (treeNode instanceof MyTreeItem) {
menu.add(new JMenuItem("This is my tree item: " + treeNode.toString()));
}
return menu;
}
I'm trying to use a JButton as an editor within a JComboBox. On Mac OS X this looks fine, but on Windows using the system look and feel, there is an ugly gap left between the JButton editor and the combo button itself:
This is the test code used to produce the dialog:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class ButtonEditorTest implements Runnable {
String[] items = {"One", "Two", "Three"};
ComboBoxModel model;
ButtonEditorTest() {
// our model, kept simple for the test
model = new DefaultComboBoxModel(items);
// create the UI on the EDT
SwingUtilities.invokeLater(this);
}
// creates UI on the event dispatch thread
#Override
public void run() {
JComboBox comboBox = new JComboBox(model);
comboBox.setEditable(true);
comboBox.setEditor(new ComboButtonEditor());
JFrame frame = new JFrame("JComboBox with JButton editor test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(comboBox, BorderLayout.NORTH);
frame.setSize(200, 100);
frame.setVisible(true);
}
public static void main(String[] args) throws Exception {
String lookAndFeelClassName = UIManager.getSystemLookAndFeelClassName();
UIManager.setLookAndFeel(lookAndFeelClassName);
new ButtonEditorTest();
}
class ComboButtonEditor implements ComboBoxEditor {
private JButton button = new JButton();
private Object item;
#Override
public void addActionListener(ActionListener arg0) {
// not needed for UI test
}
#Override
public Component getEditorComponent() {
return button;
}
#Override
public Object getItem() {
return item;
}
#Override
public void removeActionListener(ActionListener arg0) {
// not needed for UI test
}
#Override
public void selectAll() {
// not needed for UI test
}
#Override
public void setItem(Object item) {
this.item = item;
button.setText(item.toString());
}
}
}
For some reason the Window LAF is overriding the default layout of the button. This results in the button being narrower. However, the width of the editor is not increased to account for the narrower button so the gap appears. Here is the code from the WindowsComboBoxUI:
protected LayoutManager createLayoutManager() {
return new BasicComboBoxUI.ComboBoxLayoutManager() {
public void layoutContainer(Container parent) {
super.layoutContainer(parent);
if (XPStyle.getXP() != null && arrowButton != null) {
Dimension d = parent.getSize();
Insets insets = getInsets();
int buttonWidth = arrowButton.getPreferredSize().width;
arrowButton.setBounds(WindowsGraphicsUtils.isLeftToRight((JComboBox)parent)
? (d.width - insets.right - buttonWidth)
: insets.left,
insets.top,
buttonWidth, d.height - insets.top - insets.bottom);
}
}
};
}
A better layout might be something like:
comboBox.setUI( new WindowsComboBoxUI()
{
#Override
protected LayoutManager createLayoutManager()
{
return new BasicComboBoxUI.ComboBoxLayoutManager()
{
public void layoutContainer(Container parent)
{
super.layoutContainer(parent);
System.out.println(editor.getBounds());
System.out.println(arrowButton.getBounds());
// if (XPStyle.getXP() != null && arrowButton != null)
// {
Dimension d = parent.getSize();
Insets insets = getInsets();
int buttonWidth = arrowButton.getPreferredSize().width;
boolean isLeftToRight = parent.getComponentOrientation().isLeftToRight();
arrowButton.setBounds(isLeftToRight
? (d.width - insets.right - buttonWidth)
: insets.left, insets.top, buttonWidth, d.height - insets.top - insets.bottom);
System.out.println(editor.getBounds());
System.out.println(arrowButton.getBounds());
Dimension size = editor.getSize();
editor.setSize(arrowButton.getLocation().x - 1, size.height);
// }
}
};
}
});
I added some output to show how the editor width changes before/after the XP adjustment. Also I don't know how to check for the XP LAF since the XPStyle class is not public.
The imports for the LAF:
import javax.swing.plaf.basic.*;
import com.sun.java.swing.plaf.windows.*;
I think if you change the place of the BorderLayout you add to, to CENTER, like this:
frame.getContentPane().add(comboBox, BorderLayout.CENTER);
It will solve that issue.
The button might grow a bit bigger though, but you can solve that by readjusting the size values if necessary.