Tooltip with ControlP5

I want to add tooltip to GUI built using ControlP5.
I found that some old examples (from around 2011) of tooltip in ControlP5 examples say that tooltip is not working as of now.

I want to know if it is still not working in 2022 ?
And if not, is there anyway to implement tooltip ?

My Examples/ControlP5/Tooltip demo doesn’t work either. The following Swing button has a workable tooltip.

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

javax.swing.JFrame frame;
java.awt.Canvas canvas;

void buildWnd() {
  JButton btn = new JButton("Press me.");
  btn.setBounds(50, 100, 100, 30);
  btn.setToolTipText("Push Button");
  frame.add(btn);
  // **** Action **** //
  btn.addActionListener( new ActionListener() {
    void actionPerformed(ActionEvent actionEvent) {
      println("Button was pressed.");
    }
  });
  btn.repaint();
}

void setup() {
  frame = (javax.swing.JFrame) ((processing.awt.PSurfaceAWT.SmoothCanvas) surface.getNative()).getFrame();
  canvas = (processing.awt.PSurfaceAWT.SmoothCanvas) ((processing.awt.PSurfaceAWT)surface).getNative();
  frame.setBounds(500, 300, 300, 300);
  frame.remove(canvas);

  javax.swing.SwingUtilities.invokeLater(new Runnable() {
    public void run() {
      buildWnd(); // Builds components on EventDispatchThread
    }
  });
}

I am getting following error while running this in Processing 3.5.4. Can you help with this.
Error: Cannot reduce the visibility of the inherited method from ActionListener

By the way, can it be used to display tooltip on GUI built using ControlP5 ?
(Sorry, I am new to java.)

Explicitly declare affected method as public:

1 Like

@GoToLoop has the solution. Add the word ‘public’ in front of ‘actionPerformed’:

public void actionPerformed(ActionEvent actionEvent) {
      println("Button was pressed.");
    }

To the best of my knowledge I don’t think you can add it to a ControlP5 control; I think you would need to contact the author.

In general PDE’s preprocessor is able to automatically prefix methods w/ access keyword public.

But b/c the functional interface ActionListener is having its method actionPerformed() being implemented via anonymous instantiation the preprocessor fails to detect that it’s a method.

However, if we move the ActionListener instantiation outside buildWnd() and assign it to a “global” variable, PDE’s preprocessor will be able to prefix actionPerformed() w/ keyword public:

static final ActionListener BUTTON_EVENT = new ActionListener() {
  void actionPerformed(final ActionEvent evt) { // no explicit public needed!
    println(evt);
  }
};

final Runnable createButton = new Runnable() {
  void run() { // no explicit public needed!
    buildWnd();
  }
};
btn.addActionListener(BUTTON_EVENT);
SwingUtilities.invokeLater(createButton);

Same for method run() from functional interface Runnable.

1 Like

I’m sure you’re correct, but this runs in 3.5.4 without error on my system:

  btn.addActionListener( new ActionListener() {
    public void actionPerformed(ActionEvent actionEvent) {
      println("Button was pressed.");
    }
  });

Yes, after you explicitly declare method actionPerformed() as public! :wink:

Yes, but that’s different from what you posted (and a lot easier!). I normally do all my testing in the latest version of Processing 4.0b8 where it is not a problem (I know you like old stuff).

Of course just adding public is indeed a lot easier!

I was just making a point about PDE’s preprocessor failing to automatically prefix public in this particular anonymous instantiation edge case.

My example is a boilerplate workaround to make PDE’s preprocessor finally be able to detect that both actionPerformed() & run() are methods and have keyword public automatically applied to them behind-the-scene.

As I already had mentioned on a previous topic, after P4 reaches a stable release & folks here stop creating topics about P4 bugs every single day, I’ll happily install it in my computer, as I did to all the other major releases!

1 Like

I never would have figured that out on my own, so I greatly appreciate your help and insight. Thanks.

1 Like

Thank you both of you for the input.

By the way, I was going through ControlP5 examples.
And I found Canvas example. It was doing something close to tooltip.

So I modified it to do tooltip.
Kindly check it out and give feedback.

will this kind of implementation make the program run slow ? (if I do for 20-30 buttons?)
is there a better way of implementing this ?

import controlP5.*;

ControlP5 cp5;
Button b1;

ControlWindow controlWindow;

Canvas cc;

// MyCanvas, your Canvas render class
class MyCanvas extends Canvas {

  int y;
  int mx = 0;
  int my = 0;
  long count;
  int mouseStopDelay = 20;
  public void setup(PGraphics pg) {
    y = 200;
  }  

  public void update(PApplet p) {
    mx = p.mouseX;
    my = p.mouseY;
  }

  public void draw(PGraphics pg) {
    // renders a square with randomly changing colors
    // make changes here.
    if(checkMouseNotMoving()){
      if(checkMouseOverController(b1)){
        pg.fill(100);
        pg.rect(mouseX, mouseY, 240, 30);
        pg.fill(255);
        pg.text("This toottip text is drawn by MyCanvas", mouseX+20,mouseY+20);
      }
    }
  }
  
  boolean checkMouseOverController(Button b){
    boolean result=false;
    float[] position =b.getPosition();
    boolean xCheck = (mouseX>position[0] && mouseX < position[0]+b.getWidth());
    boolean yCheck = (mouseY>position[1] && mouseY < position[1]+b.getHeight());
    
    if(xCheck && yCheck)
      result = true;
    return result;
  }
  
  boolean checkMouseNotMoving(){
    boolean result=false;
    if(mouseX==pmouseX && mouseY==pmouseY){
       if(count>mouseStopDelay){
         result = true;
       }else{
         count++;
       }
    }else{
      count=0;
      result = false;
    }
    return result;
  }
}




void setup() {
  size(400, 400);
  frameRate(30);
  cp5 = new ControlP5(this);

  // create a control window canvas and add it to
  // the previously created control window.  
  cc = new MyCanvas();
  //cc.pre(); // use cc.post(); to draw on top of existing controllers.
  cc.post(); // use cc.post(); to draw on top of existing controllers.
  cp5.addCanvas(cc); // add the canvas to cp5
  b1=cp5.addButton("Hello").setPosition(50,50).setSize(100,100);
}

void draw() {
  background(250);
  fill(60);
  rect(100, 100, 200, 200);
}

Looks like a creative and reasonable way to solve your problem. The following code will create a button grid of 25 buttons for you to practice on and answer the question whether it can handle the load. Runs in 4.0b8; not tested in 3.5.4 but should work ok.

import controlP5.*;

ControlP5 cp5;
final int rows = 5;
final int cols = 5;

final int _btnGridX = 40;
final int _btnGridY = 40;
final int _btnW = 100;
final int _btnH = 100;

Button[] btn = new Button[rows*cols];
color BLUE = color(64,124,188);

void btnGrid() {
  int left = 0;
  int top = 0;
  int vg = 30; // Space between cols (vert.gutter)
  int hg = 30; // Space between rows (horz.gutter)
  int id = 0;
  
  for (int k = 0; k < cols; k++) {
    for (int j = 0; j < rows; j++) {
      left = _btnGridX + j * (_btnW + vg);
      top = _btnGridY + k * (_btnH + hg);      
      btn[id]= cp5.addButton(str(id)).setPosition(left,top).setSize(_btnW,_btnH);
      id++;
    }
  } 
}

void setup() {
  size(720,720);
  background(BLUE);
  cp5 = new ControlP5(this);
  btnGrid();
}

void draw() {
  
}
2 Likes