G4P Close Window Vs Hide Window

Hi Peter, This was discussed in another thread, but I felt it needed its own dedicated thread.

I want to be able to handle when a user clicks the “x” button to close a GWindow, and also be able to “hide” it using a button if they click the button.

I’ve added a close handler, but it does not trigger when the “x” is clicked on a GWindow. Neither does it trigger when the window is hidden.

Here’s an example:

// Need G4P library
import g4p_controls.*;
// You can remove the PeasyCam import if you are not using
// the GViewPeasyCam control or the PeasyCam library.
import peasy.*;

GWindow window = null;
GButton button0; 

public void setup(){
  size(480, 320, JAVA2D);
  createGUI();
  customGUI();
  // Place your setup code here
  
}

public void draw(){
  background(230);
  
}

// Use this method to add additional statements
// to customise the GUI controls
public void customGUI(){

}

void showGWindow() {
  
  if (window == null) {
    window = GWindow.getWindow(this, "Title", 100, 100, 200, 100, JAVA2D);
    window.setAlwaysOnTop(true);
    window.setActionOnClose(G4P.HIDE_WINDOW);//CLOSE_WINDOW//HIDE_WINDOW
    //window.addMouseHandler(this, "windowMouse");
    //window.addKeyHandler(this, "windowKey");
    window.addDrawHandler(this, "windowDraw"); 
    window.addOnCloseHandler(this, "windowClose");
    
    button0 = new GButton(window, 50, 20, 80, 30);
    button0.setText("OK");
    button0.addEventHandler(this, "button0_click1");
  }
  else {
    window.setVisible(true);
    println("not null");
  }
  
}//showGWindow

void windowDraw(PApplet app, GWinData data) {

}

void button0_click1(GButton source, GEvent event) {
  window.setVisible(false);  
}

public void windowClose(GWindow source){
  println("windowClose called!");     
}

/* =========================================================
 * ====                   WARNING                        ===
 * =========================================================
 * The code in this tab has been generated from the GUI form
 * designer and care should be taken when editing this file.
 * Only add/edit code inside the event handlers i.e. only
 * use lines between the matching comment tags. e.g.

 void myBtnEvents(GButton button) { //_CODE_:button1:12356:
     // It is safe to enter your event code here  
 } //_CODE_:button1:12356:
 
 * Do not rename this tab!
 * =========================================================
 */

public void button3_click1(GButton source, GEvent event) { //_CODE_:button3:413930:
  println("button3 - GButton >> GEvent." + event + " @ " + millis());
  showGWindow();
} //_CODE_:button3:413930:



// Create all the GUI controls. 
// autogenerated do not edit
public void createGUI(){
  G4P.messagesEnabled(false);
  G4P.setGlobalColorScheme(GCScheme.BLUE_SCHEME);
  G4P.setMouseOverEnabled(false);
  surface.setTitle("Sketch Window");
  button3 = new GButton(this, 197, 145, 80, 30);
  button3.setText("open win");
  button3.addEventHandler(this, "button3_click1");
}

// Variable declarations 
// autogenerated do not edit
GButton button3; 

What I’d like is to be able to trap for both situations, when the user clicks the “x” on the OS dialog box window, or when the button OK is clicked.

Is there a way to handle this correctly?

Thanks,

Mike

Worked for me with your code.

The close handler method is only called when the window is closed i.e. the action on close is CLOSE_WINDOW. If action on close is HIDE_WINDOW or KEEP_OPEN then the close handler is not closed. The hint is in the function name :grinning:

Having said that your code also worked as expected.

I have refactored uour code and renamed some things to make it easier to follow. Here it is -

import g4p_controls.*;
import java.awt.Font;

GWindow window = null;
GButton btnOpenWindow, btnCloseWindow, btnAction;

int what_to_do_on_close = G4P.CLOSE_WINDOW ; //HIDE_WINDOW//KEEP_OPEN;

public void setup() {
  size(480, 320, JAVA2D);
  G4P.setDisplayFont ("Arial", Font.PLAIN, 16);
  createGUI();
  fill(0);
  textSize(16);
  textAlign(CENTER, CENTER);
}

public void draw() {
  background(230);
  String actionText = "UNDEFINED";
  switch(what_to_do_on_close) {
  case G4P.CLOSE_WINDOW:
    actionText = "CLOSE_WINDOW";
    break;
  case G4P.HIDE_WINDOW:
    actionText = "HIDE_WINDOW";
    break;
  case G4P.KEEP_OPEN:
    actionText = "KEEP_OPEN";
    break;
  }
  text("Action when trying to close window", 0, 10, width, 50);
  text(actionText, 0, 40, width, 50);
}

public void showGWindow() {
  if (window == null) {
    window = GWindow.getWindow(this, "Title", 400, 300, 300, 120, JAVA2D);
    window.setAlwaysOnTop(true);
    // Change 'what_to_do_on_close' above
    window.setActionOnClose(what_to_do_on_close);
    //window.addMouseHandler(this, "windowMouse");
    //window.addKeyHandler(this, "windowKey");
    window.addDrawHandler(this, "windowDraw");
    window.addOnCloseHandler(this, "windowCloseAction");

    btnAction = new GButton(window, 50, 70, 120, 30);
    btnAction.setText("HIDE window");
    btnAction.addEventHandler(this, "btnAction_Click");
    println("Create window");
  } else {
    window.setVisible(true);
    println("Make window visible");
  }
}//showGWindow

public void windowDraw(PApplet app, GWinData data) {
  app.background(200, 255, 200);
}

public void btnAction_Click(GButton source, GEvent event) {
  System.out.println("Hiding window");
  window.setVisible(false);
}

public void windowCloseAction(GWindow source) {
  println("windowCloseAction called!");
  window = null;
}

public void btnOpenWindow_Click(GButton source, GEvent event) {
  showGWindow();
}

public void btnCloseWindow_Click(GButton source, GEvent event) {
  println("Close window button clicked");
  window.close();
}

public void createGUI() {
  G4P.messagesEnabled(false);
  G4P.setGlobalColorScheme(GCScheme.BLUE_SCHEME);
  G4P.setMouseOverEnabled(false);
  surface.setTitle("Sketch Window");
  btnOpenWindow = new GButton(this, 60, 145, 160, 30);
  btnOpenWindow.setText("OPEN  window");
  btnOpenWindow.addEventHandler(this, "btnOpenWindow_Click");
  btnCloseWindow = new GButton(this, 260, 145, 160, 30);
  btnCloseWindow.setText("CLOSE window");
  btnCloseWindow.addEventHandler(this, "btnCloseWindow_Click");
}
1 Like

Hi Peter, indeed your solution works by changing that one line of code to CLOSE_WINDOW. And your code is also cleaner; thank you for that.

I had tried that before and yes it works - but then subsequent opens of the window render the button dead (as well as any controls that could have been added). This you explained is expected behavior with the CLOSE_WINDOW method, in the previous post (thank you for that).

I realize I am pushing against the philosophy and design of the situation here, so I don’t know if there’s a solution then.

Because I want to be able to show/hide the windows, so that subsequent reopens of the windows allow all the controls to remain operational, if I use the CLOSE_WINDOW method then the controls are all dead. Which is what I need to avoid. That’s why I was using the HIDE_WINDOW method.

Is there any other way to trap for the close “X” OS button other than the CLOSE_WINDOW method? I realize even typing this that it sounds foolish.

If I could just get the window controls to remain viable after a user clicks the “X” (and then reopens the window) that would be amazing.

Alternately, If I were to totally “dispose” of the windows (not sure how), could I recreate them each time with the same name, or they would not ever work again?

Again this goes back to the attempt at messaging, which you told me to stop coding everything else and focus on it, which I’m doing now.

I don’t think the GPanels are going to cut it because they are so different from regular OS messaging boxes, although they are cool. I suppose I could make them work, but again, I’m trying to create an app that looks similar to expected OS apps as much as possible.

Thank you for your time - sincerely - thank you so much for the incredible amount of time you’ve spent helping me here, regardless the outcome.

I so appreciate you.

All the best,

Mike

Some things to make clear first

  1. CLOSE_WINDOW, HIDE_WINDOW and KEEP_OPEN are not methods, they are simple integer constants used as flags.
  2. the setActionOnClose(...) method indicates which of the actions from (1) should be attempted when
    • the OS’s close button (X for Windows; red circle for osx) is clicked
    • the G4P library method window.close() method is called.
  3. The G4P library method window.forceClose() ignores the flag and closes the window respectively.

Once a window is closed that’s it, all the controls are closed as well. There is nothing you can do but recreate the window from scratch. If you look at your showGWindow method the solution becomes clear.

The window is created if the window reference is null, so changing the reference to null when the window closes will solve your problem. So two step

  1. Create a close handler method with addOnCloseHandler
  2. In the on-close-handler method specified in (1) add a statement to nullify the window reference.

I have changed the sketch code in my last post to simplify testing what I have side in this post.

// This is the method specified in the call to
// addOnCloseHandler(method_name)
public void windowCloseAction(GWindow source) {
  println("windowCloseAction called!");
  window = null;
}
1 Like

Peter I am so grateful for your time and expertise - honestly I can’t thank you enough :smiling_face_with_three_hearts:

I’m still working my way through your updated example, but do get a null pointer error when clicking the Close Window button:

################  EXCEPTION IN EVENT HANDLER  ################
An error occured during execution of the eventhandler:
CLASS: sketch_240714a   METHOD: btnCloseWindow_Click
	Caused by java.lang.NullPointerException: Cannot invoke "g4p_controls.GWindow.close()" because "this.window" is null
	sketch_240714a.btnCloseWindow_Click(sketch_240714a.java:97)
	java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
	java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	java.base/java.lang.reflect.Method.invoke(Method.java:568)
	g4p_controls.GAbstractControl.fireEvent(Unknown Source)
	g4p_controls.GButton.mouseEvent(Unknown Source)
	g4p_controls.GWindowImpl.mouseEvent(Unknown Source)
	jdk.internal.reflect.GeneratedMethodAccessor4.invoke(Unknown Source)
	java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	java.base/java.lang.reflect.Method.invoke(Method.java:568)
	processing.core.PApplet$RegisteredMethods.handle(PApplet.java:1309)
	processing.core.PApplet.handleMethods(PApplet.java:1456)
	processing.core.PApplet.handleMouseEvent(PApplet.java:2368)
	processing.core.PApplet.dequeueEvents(PApplet.java:2261)
	processing.core.PApplet.handleDraw(PApplet.java:2104)
	processing.awt.PSurfaceAWT$9.callDraw(PSurfaceAWT.java:1386)
	processing.core.PSurfaceNone$AnimationThread.run(PSurfaceNone.java:356)
##############################################################

In any case I’m going to spend more time with your example and again thank you so much :clap: :heartbeat: :star_struck:

EDIT: I just changed the action on the btnCloseWindow:

public void btnCloseWindow_Click(GButton source, GEvent event) {
  println("Close window button clicked");
  if (window != null) window.close();
}

No null pointer error now.

Thank you again so much!

By the way are there any memory concerns with this method - will this become a resource hog with using this for lots of windows?

Also, is there any way to add the window.addOnCloseHandler(this, "windowCloseAction"); to the main sketch window as well? That way I could trap for the whole sketch being closed with the “X” as well…

Thanks again! You’re so kind.

Mike

Attempting to close a window that no longer exists. Well spotted :smile:

Not if you are careful. Consider an application that has two global GWindow references e.g.

GWindow w1;
GWindow w2;

Now both of these are initialized to null by Java. If you now create a window like this
w1 = GWindow.getWindow(this, "Title", 400, 300, 300, 120, JAVA2D);

w1 is no longer null and the memory for the window is kept and managed by Java.

Now if at some stage you use w1.forceClose(); G4P nulls all internal references to the window so the only reference is w1. While there is an active reference Java will continue to keep the memory available, If after closing the window we do w1 = null; there is no longer an active reference to the window so Java’s garbage collector will release the memory back to the system heap for later reuse.
Now consider the following statements

w2 = w1;
w1.forceClose();
w1 = null;

Although we have told G4P we no longer want this window Java will keep the memory because we still have an active reference to it i.e. w2. This is called a memory leak

To relapse the memory we need to nullify the w2 reference. i.e. w2 = null;

This means you have to be careful with object refernces.

2 Likes

What most it be like to be so incredibly brilliant?

Thank you so much for sharing!

Also, is there any way to add the window.addOnCloseHandler(this, “windowCloseAction”); to the main sketch window as well? That way I could trap for the whole sketch being closed with the “X” as well…

AFAIK no butyou can use the exit() method in the main sketch to perform close down actions when the window is closed.

// Executed when the application closes down
public void exit(){
  // perform any close down actions

  // the last line must call the parent exit() method
  super.exit();
}

I am confident there is a way to get rid of the X button and even create borderless apps but you have to be careful because some of the techniques are OS specific. If you want to try this then use the search on Processing’s home page rather tan search this forum directly. You generally get better results that way.

If you have no joy then create a new topic about how to get rid of the close button. Since this is a Processing thing don’t mention G4P because it will just muddy the waters.

2 Likes

I appreciate this very much.

What I’ve noticed and from the documentation is this is only for a couple last minute variable updates type of thing - it quits after the draw() method finishes it’s current cycle.

I know I’m asking too much - and this is now beyond the scope of G4P for sure so I may post this as a general thread here - but it would be fantastic to simply have a way to simply detect when a person clicks the “x” - and the do whatever processes I wanted after.

The reason in this case is because there are files to save, filenames to ask the user to provide, etc, and very little of this works correctly calling from exit(). Sketch ends up quitting before the rest of the “closing” processes I want to provide have run.

Thank you again Peter; you are an absolute treasure.

mike