# Class Within A Class Demo (Menu / Button)

**URL:** https://discourse.processing.org/t/class-within-a-class-demo-menu-button/42782
**Category:** Gallery
**Created:** [September 19, 2023, 3:08am UTC](https://discourse.processing.org/t/class-within-a-class-demo-menu-button/42782 "2023-09-19T03:08:06Z")
**Posts on this page:** 14
**Page:** 1

<div class="post-metadata">

### Author: ![svan](https://avatars.discourse-cdn.com/v4/letter/s/82dd89/32.png) [@svan](https://discourse.processing.org/u/svan)
#### Post date: [September 19, 2023, 3:08am UTC](https://discourse.processing.org/t/class-within-a-class-demo-menu-button/42782/1 "2023-09-19T03:08:06Z")

</div>

```auto
// Demonstrates Button class inside of MenuBar class
// https://docs.oracle.com/javase/tutorial/java/javaOO/nested.html

color BLUE = color(64, 124, 188);
color LTGRAY = color(185, 180, 180);
color YELLOW = color(245, 250, 13);
color RED = color(255, 0, 0);
color BLACK = color(0, 0, 0);
color WHITE = color(255, 255, 255);
color GREEN = color(0, 255, 0);

MenuBar menuBar;
MenuBar.Button _addBtn;
MenuBar.Button _saveBtn;
MenuBar.Button _clearBtn;
MenuBar.Button _openBtn;

class MenuBar {
  int x, y, w, h;
  color bkgrnd;
  
  // Constructor 
  MenuBar(int xpos, int ypos, int wide, int ht, color background) {
    x = xpos;
    y = ypos;
    w = wide;
    h = ht;
    bkgrnd = background;
  }

  void display() {
    noStroke();
    fill(bkgrnd);
    rect(x, y, w, h);
    stroke(0);
  }

  class Button {
    float x, y, w, h;
    String title;
    color btnColor;
    color txtColor;
    float txtSize;

    // Constructor
    Button(int xpos, int ypos, float wt, float ht, String titleStr, color background, color foreground, float textSize) {
      x = xpos;
      y = ypos;
      w = wt;
      h = ht;
      title = titleStr;
      btnColor = background;
      txtColor = foreground;
      txtSize = textSize;
    }

    void display() {
      fill(btnColor); // button color
      noStroke();
      rect( x, y, w, h, 15); // rounded rect
      fill(txtColor); // text color
      textSize(txtSize);
      textAlign(CENTER);
      text(title, x, y, w, h);
    }
  } // End of Button class
  
} // End of MenuBar class

void setup() {
  size(600, 600);
  menuBar = new MenuBar(0, 0, width, 60, color(180));
  _addBtn = menuBar.new Button(30, 15, 80, 24, "Add", YELLOW, BLACK, 18);
  _saveBtn = menuBar.new Button(120, 15, 80, 24, "Save", GREEN, BLACK, 18);
  _clearBtn = menuBar.new Button(210, 15, 80, 24, "Clear", RED, BLACK, 18);
  _openBtn = menuBar.new Button(300, 15, 80, 24, "Open", WHITE, BLACK, 18);
}

void draw() {
  menuBar.display();
  _addBtn.display();
  _saveBtn.display();
  _clearBtn.display();
  _openBtn.display();
}

void mousePressed() {
  if ((mouseX >= _addBtn.x) && (mouseX <= _addBtn.x + _addBtn.w) && (mouseY >= _addBtn.y) && (mouseY <= _addBtn.y + _addBtn.h)) {
    println("Add btn hit.");
  }
  if ((mouseX >= _saveBtn.x) && (mouseX <= _saveBtn.x + _saveBtn.w) && (mouseY >= _saveBtn.y) && (mouseY <= _saveBtn.y + _saveBtn.h)) {
    println("Save btn hit.");
  }
  if ((mouseX >= _clearBtn.x) && (mouseX <= _clearBtn.x + _clearBtn.w) && (mouseY >= _clearBtn.y) && (mouseY <= _clearBtn.y + _clearBtn.h)) {
    println("Clear btn hit.");
  }
  if ((mouseX >= _openBtn.x) && (mouseX <= _openBtn.x + _openBtn.w) && (mouseY >= _openBtn.y) && (mouseY <= _openBtn.y + _openBtn.h)) {
    println("Open btn hit.");
  }
}

```

---

<div class="post-metadata">

### Author: ![GoToLoop](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/gotoloop/32/86_2.png) [@GoToLoop](https://discourse.processing.org/u/GoToLoop)
#### Post date: [September 19, 2023, 5:21am UTC](https://discourse.processing.org/t/class-within-a-class-demo-menu-button/42782/2 "2023-09-19T05:21:57Z")

</div>

I’ve moved class Button outside class MenuBar and your code still worked after some few small changes to variables’ declaration & instantiation: 🧑‍🏭

```auto
MenuBar menuBar;
Button _addBtn;
Button _saveBtn;
Button _clearBtn;
Button _openBtn;

```

```auto
void setup() {
  size(600, 600);
  menuBar = new MenuBar(0, 0, width, 60, color(180));
  _addBtn = new Button(30, 15, 80, 24, "Add", YELLOW, BLACK, 18);
  _saveBtn = new Button(120, 15, 80, 24, "Save", GREEN, BLACK, 18);
  _clearBtn = new Button(210, 15, 80, 24, "Clear", RED, BLACK, 18);
  _openBtn = new Button(300, 15, 80, 24, "Open", WHITE, BLACK, 18);
}

```

So I don’t see the point in forcing Button to be an inner class of MenuBar! 🤷‍♂️

---

<div class="post-metadata">

### Author: ![svan](https://avatars.discourse-cdn.com/v4/letter/s/82dd89/32.png) [@svan](https://discourse.processing.org/u/svan)
#### Post date: [September 19, 2023, 5:47am UTC](https://discourse.processing.org/t/class-within-a-class-demo-menu-button/42782/3 "2023-09-19T05:47:35Z")

</div>

You are correct and the point is well taken. I mainly just wanted to see if I could get a class within a class to work. It may have limited usefulness. The original MenuBar class used a function to create the buttons and that worked pretty well (made draw() simpler), except when it came time to trap button clicks in mousePressed() I had to copy all the coordinates from the class and hard code them. I like the myBtn.x, myBtn.y etc. coordinate method better, but the down side is I have to have a separate Button class either outside or within the MenuBar class. Thanks for taking the time to look at it.

---

<div class="post-metadata">

### Author: ![GoToLoop](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/gotoloop/32/86_2.png) [@GoToLoop](https://discourse.processing.org/u/GoToLoop)
#### Post date: [September 19, 2023, 6:10am UTC](https://discourse.processing.org/t/class-within-a-class-demo-menu-button/42782/4 "2023-09-19T06:10:32Z")

</div>

> [@svan](#):
>
> I mainly just wanted to see if I could get a class within a class to work.

Don’t forget that all classes within a '.pde" file are already inner classes of a PApplet subclass.

So, your class MenuBar is an inner class of subclass PApplet.  
And class Button is an inner class of both class MenuBar & subclass PApplet.

That’s why both MenuBar & Button have direct access to all PApplet’s members, like **fill()**, **rect()**, etc., w/o needing an explicit PApplet reference.

---

<div class="post-metadata">

### Author: ![mnse](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/mnse/32/16520_2.png) [@mnse](https://discourse.processing.org/u/mnse)
#### Post date: [September 19, 2023, 7:32am UTC](https://discourse.processing.org/t/class-within-a-class-demo-menu-button/42782/5 "2023-09-19T07:32:44Z")

</div>

Hi @svan,

Maybe you could reconsider your approach to your menu class. Typically, a class is designed to handle all associated tasks through its interface. In the case of a menu class, this means that instead of having the menu and buttons separate, the menu should manage the buttons. I currently don’t have the time to detail a fully clean implementation, but the following quick hack should give you an idea of what I mean…

Cheers  
— mnse

![19-09-2023_09-36-17](https://canada1.discourse-cdn.com/flex036/uploads/processingfoundation1/original/3X/f/d/fd02b5ffab02dff2f232616ec84814ca41c3f92a.gif)

```auto
// Demonstrates Button class inside of MenuBar class
// https://docs.oracle.com/javase/tutorial/java/javaOO/nested.html

color BLUE = color(64, 124, 188);
color LTGRAY = color(185, 180, 180);
color YELLOW = color(245, 250, 13);
color RED = color(255, 0, 0);
color BLACK = color(0, 0, 0);
color WHITE = color(255, 255, 255);
color GREEN = color(0, 255, 0);

color MENU_DEFAULT_BGCOLOR = color(180);
int BTN_DEFAULT_WIDTH = 80;
int BTN_DEFAULT_HEIGHT = 24;
int BTN_DEFAULT_TEXTSIZE = 18;

MenuBar menuBar;

class MenuBar {
  int x, y, w, h;
  int marginX, marginY;
  color bkgrnd;
  ArrayList<Button> buttons;
  
  // Constructor 
  MenuBar(int xpos, int ypos, color background) {
    x = xpos;
    y = ypos;
    w = 0;
    h = 0;
    marginX=10;
    marginY=10;
    bkgrnd = background;
    buttons = new ArrayList<Button>();
  }
  
  void addButton(String titleStr, color background, color foreground) {
    buttons.add(new Button(titleStr, background, foreground));
    updateParams();
  }

  void updateParams() {
    int bx = x+marginX;
    w=marginX;
    for (Button b: buttons) {
      b.x = bx;
      b.y = y+marginY;
      w+=b.w+marginX;
      if (b.h > h)
        h=b.h+2*marginY;
      bx+=b.w+marginX;      
    }
    w+=marginX;
  }

  void handleMouseEvent(int mx, int my, boolean isPressed) {
    for (Button b:buttons) {
      if (isPressed)
        b.handlePressed(mx,my);
      else
        b.handleHover(mx,my);
    }
  }

  void display() {
    noStroke();
    fill(bkgrnd);
    rect(x, y, w, h);
    for (Button b:buttons)
      b.display();
  }

  class Button {
    int x, y, w, h;
    String title;
    color btnColor;
    color txtColor;
    float txtSize;
    boolean hover;

    // Constructor
    Button(String titleStr, color background, color foreground) {
      w = BTN_DEFAULT_WIDTH;
      h = BTN_DEFAULT_HEIGHT;
      title = titleStr;
      btnColor = background;
      txtColor = foreground;
      txtSize = BTN_DEFAULT_TEXTSIZE;
      hover=false;
    }

    void handlePressed(int mx, int my) {
      if (mx > x && mx < x+w && my > y && my < y+h)
        buttonPressed(Button.this);      
    }

    void handleHover(int mx, int my) {
      hover = false;
      if (mx > x && mx < x+w && my > y && my < y+h)
        hover = true;      
    }

    void display() {
      pushStyle();
      fill(btnColor); // button color
      noStroke();
      if (hover) {
        strokeWeight(2);
        stroke(txtColor);
      }
        
      rect(x, y, w, h, 15); // rounded rect
      fill(txtColor); // text color
      textAlign(CENTER,CENTER);
      textSize(txtSize);
      text(title, x+w/2, y+h/2);
      popStyle();
    }
  } // End of Button class
  
} // End of MenuBar class

void setup() {
  size(400, 50);
  menuBar = new MenuBar(10, 10, color(180));
  menuBar.addButton("Add",YELLOW,BLACK);
  menuBar.addButton("Save",GREEN,BLACK);
  menuBar.addButton("Clear",RED,BLACK);
  menuBar.addButton("Open",WHITE,BLACK);
}

void draw() {
  background(225);
  menuBar.display();
}

public void buttonPressed(MenuBar.Button b) {
  println("Button pressed: " + b.title);
}

void mousePressed() {
  menuBar.handleMouseEvent(mouseX,mouseY, true);
}

void mouseMoved() {
  menuBar.handleMouseEvent(mouseX,mouseY, false);
}

```

---

<div class="post-metadata">

### Author: ![svan](https://avatars.discourse-cdn.com/v4/letter/s/82dd89/32.png) [@svan](https://discourse.processing.org/u/svan)
#### Post date: [September 19, 2023, 7:56am UTC](https://discourse.processing.org/t/class-within-a-class-demo-menu-button/42782/6 "2023-09-19T07:56:14Z")

</div>

Thank you for this concept and code.

---

<div class="post-metadata">

### Author: ![Chrisir](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/chrisir/32/45_2.png) [@Chrisir](https://discourse.processing.org/u/Chrisir)
#### Post date: [September 19, 2023, 8:48am UTC](https://discourse.processing.org/t/class-within-a-class-demo-menu-button/42782/7 "2023-09-19T08:48:24Z")

</div>

> [@mnse](#):
>
> In the case of a menu class, this means that instead of having the menu and buttons separate, the menu should manage the buttons.

That’s what I thought.

- Buttons belong into the Menu class and the class handles the buttons

- Usage of Arraylist is recommended

- You can evaluate the return type b.title of the button using switch (){…

- I also would advice against placing one class inside another.

- I also place the classes at the end of the Sketch

---

<div class="post-metadata">

### Author: ![GoToLoop](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/gotoloop/32/86_2.png) [@GoToLoop](https://discourse.processing.org/u/GoToLoop)
#### Post date: [September 19, 2023, 2:18pm UTC](https://discourse.processing.org/t/class-within-a-class-demo-menu-button/42782/8 "2023-09-19T14:18:46Z")

</div>

> [@mnse](#):
>
> , this means that instead of having the menu and buttons separate, the menu should manage the buttons.

Although it’s a start, I think we still need more compelling reasons to justify making Button a non-`static` inner class of MenuBar.

Again, I merely needed to cut ‘n’ paste class Button outside of MenuBar, and the whole sketch was still able to run!

The only extra tiny fix I had to do was in top-level function **buttonPressed()**'s parameter declaration, from MenuBar.Button _b_ to just Button _b_:

```auto
public void buttonPressed(Button b) {
  println("Button pressed: " + b.title);
}

```

BtW, **buttonPressed()** makes more sense as an actual method of Button itself, given it’s merely a log for field Button::_title_:

```auto
void buttonPressed() {
  println("Button pressed:", title);
}

```

If we think those 2 classes as part of a bigger library, making Button an inner class would bar users of that library to use Button directly, unless we declare it as `public`.

They’d have to accept MenuBar as the only manager of instances of Button.

IMO, that’d be very selfish, given Button doesn’t depend on MenuBar for anything!

And users should be allowed to directly manage Button if they don’t like how MenuBar does so.

---

<div class="post-metadata">

### Author: ![mnse](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/mnse/32/16520_2.png) [@mnse](https://discourse.processing.org/u/mnse)
#### Post date: [September 19, 2023, 3:14pm UTC](https://discourse.processing.org/t/class-within-a-class-demo-menu-button/42782/9 "2023-09-19T15:14:15Z")

</div>

Hi,

just some quick notes…

> [@GoToLoop](#):
>
> Again, I merely needed to cut ‘n’ paste class Button outside of MenuBar, and the whole sketch was still able to run!

Sure! But imagine that those kind of buttons are only related and designed to be used in this menu class. Sometime you don’t want internal classes to be provided to the outer world. But also in this case I would named the class MenuButton to be clear it is not a regular Button class.

* * *

> [@GoToLoop](#):
>
> ```auto
> void buttonPressed() {
> println("Button pressed:", title);
> }
> 
> ```

in this case you want a simple interface in your sketch, when a menu button is pressed, so the Button should be a parameter to identify which menu button is pressed, hence

```auto
public void buttonPressed(Button b) {
 if (b.title.equals("Add")) {
  // do something related to Add Button
 }

```

or

```auto
public void buttonPressed(String id) {
 if (id.equals("Add")) {
  // do something related to Add Button
 }
}

```

* * *

Usually I would say the MenuButtons should have a @FunctionalInterface to call it like

```auto
 menuBar.addButton("Add",YELLOW,BLACK, () -> { 
    // do stuff for Add Button here
 });

```

For a menu class to be truly good, stable and flexible, there’s much more to be done… 😉  
I would say even the MenuButton is an abstract base class (or Interface) to many (ie TextButton, ImageButton, OptionButton, etc.) 🙂

Cheers  
— mnse

---

<div class="post-metadata">

### Author: ![Chrisir](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/chrisir/32/45_2.png) [@Chrisir](https://discourse.processing.org/u/Chrisir)
#### Post date: [September 19, 2023, 5:57pm UTC](https://discourse.processing.org/t/class-within-a-class-demo-menu-button/42782/10 "2023-09-19T17:57:53Z")

</div>

my current version - a bit as described above

```auto

// Button class used by MenuBar class
// https://docs.oracle.com/javase/tutorial/java/javaOO/nested.html

final color BLUE = color(64, 124, 188);
final color LTGRAY = color(185, 180, 180);
final color YELLOW = color(245, 250, 13);
final color RED = color(255, 0, 0);
final color BLACK = color(0, 0, 0);
final color WHITE = color(255, 255, 255);
final color GREEN = color(0, 255, 0);

color MENU_DEFAULT_BGCOLOR = color(180);
int BTN_DEFAULT_WIDTH = 80;
int BTN_DEFAULT_HEIGHT = 24;
int BTN_DEFAULT_TEXTSIZE = 18;

MenuBar menuBar;
int cmd = -1;

// -------------------------------------------------------------------------------------------------

void setup() {
  size(950, 450);
  background(225);

  menuBar = new MenuBar(10, 10, LTGRAY);

  menuBar.addButton("Add", YELLOW, BLACK);
  menuBar.addButton("Save", GREEN, BLACK);
  menuBar.addButton("Clear", RED, BLACK);
  menuBar.addButton("Open", WHITE, BLACK);
}

void draw() {
  menuBar.display();
  showCmdNumber();
}

// ---------------------------

void mousePressed() {
  String myCmd =
    menuBar.handleMouseEvent(mouseX, mouseY, true);
  buttonPressed( myCmd );
}

void mouseMoved() {
  menuBar.handleMouseEvent(mouseX, mouseY, false);
}

// ---------------------------

void buttonPressed(String b) {
  cmd = -1;
  println("Button pressed: " + b);

  switch (b) {
  case "Add":
    cmd = 0;
    rect(random(width), random(height), 22, 11);
    break;
  case "Save":
    cmd = 1;
    break;
  case "Clear":
    cmd = 2;
    background(225);
    break;
  case "Open":
    cmd = 3;
    break;
  }
}

void showCmdNumber() {
  // yellow box
  fill(YELLOW);
  stroke(BLACK);
  rect (width/2-100, height/2-20, 200, 40);

  // its text
  fill(0);
  textAlign(CENTER);
  text ("command: "
    + cmd, width/2, height/2);
  textAlign(LEFT);
} //func

// =========================================================================================

class MenuBar {

  // class MenuBar is using class Button

  int x, y,
    w, h;
  int marginX, marginY;
  color bkgrnd;

  ArrayList<Button> buttons;

  // ---------------------------

  // Constructor
  MenuBar(int xpos, int ypos, color background) {
    x = xpos;
    y = ypos;
    w = 0;
    h = 0;
    marginX=10;
    marginY=10;
    bkgrnd = background;
    buttons = new ArrayList<Button>();
  } // Constructor

  // ---------------------------

  void addButton(String titleStr, color background, color foreground) {
    buttons.add(new Button(titleStr, background, foreground));
    updateParams();
  }

  void updateParams() {
    int bx = x+marginX;
    w=marginX;
    for (Button b : buttons) {
      b.x = bx;
      b.y = y+marginY;
      w+=b.w+marginX;
      if (b.h > h)
        h=b.h+2*marginY;
      bx+=b.w+marginX;
    }
    w+=marginX;
  }

  String handleMouseEvent(int mx, int my,
    boolean isPressed) {

    String result;

    for (Button b : buttons) {
      if (isPressed) {
        result = b.handlePressed(mx, my);
        if (!result.equals("NONE"))
          return result;
      } else {
        result = b.handleHover(mx, my);
        // return b.handleHover(mx, my);
      }//else
    }//for
    return "NONE";
  }

  void display() {
    noStroke();
    fill(bkgrnd);
    rect(x, y, w, h);
    for (Button b : buttons)
      b.display();
  }
} // End of MenuBar class

// =========================================================================================

class Button {

  // class Button is used by class MenuBar

  int x, y,
    w, h;
  String title;
  color btnColor;
  color txtColor;
  float txtSize;
  boolean hover;

  // ---------------------------

  // Constructor
  Button(String titleStr, color background, color foreground) {
    w = BTN_DEFAULT_WIDTH;
    h = BTN_DEFAULT_HEIGHT;
    title = titleStr;
    btnColor = background;
    txtColor = foreground;
    txtSize = BTN_DEFAULT_TEXTSIZE;
    hover=false;
  } // Constructor

  // ---------------------------

  String handlePressed(int mx, int my) {
    if (mx > x &&
      mx < x+w &&
      my > y &&
      my < y+h)
      return title;
    else
      return "NONE";
  }

  String handleHover(int mx, int my) {
    hover = false;
    if (mx > x &&
      mx < x+w &&
      my > y &&
      my < y+h) {
      hover = true;
    }

    return "NONE";
  }

  void display() {
    pushStyle();
    fill(btnColor); // button color
    noStroke();

    if (hover) {
      strokeWeight(2);
      stroke(txtColor);
    }

    rect(x, y, w, h, 15); // rounded rect
    fill(txtColor); // text color
    textAlign(CENTER, CENTER);
    textSize(txtSize);
    text(title, x+w/2, y+h/2);
    popStyle();
  }
} // End of Button class
//

```

---

<div class="post-metadata">

### Author: ![GoToLoop](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/gotoloop/32/86_2.png) [@GoToLoop](https://discourse.processing.org/u/GoToLoop)
#### Post date: [September 19, 2023, 7:03pm UTC](https://discourse.processing.org/t/class-within-a-class-demo-menu-button/42782/11 "2023-09-19T19:03:58Z")

</div>

> [@mnse](#):
>
> Usually I would say the MenuButtons should have a @FunctionalInterface to call it like…

Well, in the context you’ve implemented **buttonPressed()**, I couldn’t guess it was supposed to be some kind of callback.

In such case, a functional interface is indeed the right path.  
And class Button should have a method to register such callback.

> [@mnse](#):
>
> `if (id.equals("Add")) {`

There’s always a chance that parameter _id_ might be `null`.

So, when we are comparing a variable to a literal String, always invoke **equals()** over the literal:  
`if ("Add".equals(id)) {`

Or for multiple literal strings, prefer a `switch () / case:` block as @Chrisir did.

> [@mnse](#):
>
> Sometimes you don’t want internal classes to be provided to the outer world.

Still that’s not enough compelling reason to place it nested inside another top-level class: Just don’t declare it as `public`.

---

<div class="post-metadata">

### Author: ![svan](https://avatars.discourse-cdn.com/v4/letter/s/82dd89/32.png) [@svan](https://discourse.processing.org/u/svan)
#### Post date: [September 20, 2023, 12:38am UTC](https://discourse.processing.org/t/class-within-a-class-demo-menu-button/42782/12 "2023-09-20T00:38:31Z")

</div>

Since this post seems somewhat controversial the following is an alternative that resembles a menubar. AWT buttons are used on the JFrame of the default window and the canvas is offset downward to expose them. Button events are trapped with a separate ActionListener on each button.

```auto
import java.awt.*;
import java.awt.event.*;

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

int _wndW = 600;
int _wndH = 500;

void buildMenuBar() {

  Button addBtn = new Button("Add");
  addBtn.setBounds(30, 15, 80, 30);
  frame.add(addBtn);
  addBtn.addActionListener( new ActionListener() {
    void actionPerformed(ActionEvent actionEvent) {
      println("Add btn hit.");
    }
  }
  );

  Button saveBtn = new Button("Save");
  saveBtn.setBounds(120, 15, 80, 30);
  frame.add(saveBtn);
  saveBtn.addActionListener( new ActionListener() {
    void actionPerformed(ActionEvent actionEvent) {
      println("Save btn hit.");
    }
  }
  );

  Button clearBtn = new Button("Clear");
  clearBtn.setBounds(210, 15, 80, 30);
  frame.add(clearBtn);
  clearBtn.addActionListener( new ActionListener() {
    void actionPerformed(ActionEvent actionEvent) {
      println("Clear btn hit.");
    }
  }
  );
  
  Button openBtn = new Button("Open");
  openBtn.setBounds(300, 15, 80, 30);
  frame.add(openBtn);
  openBtn.addActionListener( new ActionListener() {
    void actionPerformed(ActionEvent actionEvent) {
      println("Open btn hit.");
    }
  }
  );
}

void setup() {
  size(_wndW, _wndH);
  surface.setTitle("AWT Components with Default AWT Canvas");
  frame = (javax.swing.JFrame) ((processing.awt.PSurfaceAWT.SmoothCanvas) surface.getNative()).getFrame();
  canvas = (processing.awt.PSurfaceAWT.SmoothCanvas) ((processing.awt.PSurfaceAWT)surface).getNative();
  frame.setBounds(100, 100, _wndW, _wndH);
  canvas.setBounds(0, 60, _wndW, _wndH - 60); 
  buildMenuBar();
}

void draw() {
  fill(0);
  rect(100, 100, 300, 200);
}

```

Output:

 ![mbar](https://canada1.discourse-cdn.com/flex036/uploads/processingfoundation1/original/3X/8/9/89c76c590bf8cacb543306c8855a848cdc05c07b.png)

---

<div class="post-metadata">

### Author: ![GoToLoop](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/gotoloop/32/86_2.png) [@GoToLoop](https://discourse.processing.org/u/GoToLoop)
#### Post date: [September 20, 2023, 12:52am UTC](https://discourse.processing.org/t/class-within-a-class-demo-menu-button/42782/13 "2023-09-20T00:52:19Z")

</div>

Re-posted w/ minimal changes so it works in P3 as well:

```auto
import java.awt.*;
import java.awt.event.*;

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

int _wndW = 600;
int _wndH = 500;

void buildMenuBar() {
  Button addBtn = new Button("Add");
  addBtn.setBounds(30, 15, 80, 30);
  frame.add(addBtn);
  addBtn.addActionListener( new ActionListener() {
    public void actionPerformed(ActionEvent actionEvent) {
      println("Add btn hit.");
    }
  }
  );

  Button saveBtn = new Button("Save");
  saveBtn.setBounds(120, 15, 80, 30);
  frame.add(saveBtn);
  saveBtn.addActionListener( new ActionListener() {
    public void actionPerformed(ActionEvent actionEvent) {
      println("Save btn hit.");
    }
  }
  );

  Button clearBtn = new Button("Clear");
  clearBtn.setBounds(210, 15, 80, 30);
  frame.add(clearBtn);
  clearBtn.addActionListener( new ActionListener() {
    public void actionPerformed(ActionEvent actionEvent) {
      println("Clear btn hit.");
    }
  }
  );

  Button openBtn = new Button("Open");
  openBtn.setBounds(300, 15, 80, 30);
  frame.add(openBtn);
  openBtn.addActionListener( new ActionListener() {
    public void actionPerformed(ActionEvent actionEvent) {
      println("Open btn hit.");
    }
  }
  );
}

void settings() {
  size(_wndW, _wndH);
}

void setup() {
  surface.setTitle("AWT Components with Default AWT Canvas");
  frame = (javax.swing.JFrame) ((processing.awt.PSurfaceAWT.SmoothCanvas) surface.getNative()).getFrame();
  canvas = (processing.awt.PSurfaceAWT.SmoothCanvas) ((processing.awt.PSurfaceAWT)surface).getNative();
  frame.setBounds(100, 100, _wndW, _wndH);
  canvas.setBounds(0, 60, _wndW, _wndH - 60); 
  buildMenuBar();
  fill(0);
}

void draw() {
  rect(100, 100, 300, 200);
}

```

---

<div class="post-metadata">

### Author: ![mnse](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/mnse/32/16520_2.png) [@mnse](https://discourse.processing.org/u/mnse)
#### Post date: [September 20, 2023, 11:56am UTC](https://discourse.processing.org/t/class-within-a-class-demo-menu-button/42782/14 "2023-09-20T11:56:57Z")

</div>

Hi,

Yet another sample menu implementation (based on @svan’s version) using swing …

Cheers  
— mnse

![20-09-2023_13-53-32](https://canada1.discourse-cdn.com/flex036/uploads/processingfoundation1/original/3X/7/4/740526fc69d470357e5f74bb0eb44bf7e8897bcb.gif)

```auto
import java.awt.*;
import java.awt.event.*;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JMenuBar;
import javax.swing.JMenu;
import javax.swing.BorderFactory;
import java.awt.geom.RoundRectangle2D;

JButton createButton(String t, int w, int h, Color bg, Color fg, ActionListener a) {
  JButton btn = new JButton(t) {
    private Shape shape;

    @Override
      protected void paintComponent(Graphics g) {
      Graphics2D g2d = (Graphics2D) g;
      g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

      g2d.setColor(getBackground());
      g2d.fillRoundRect(0, 0, getWidth(), getHeight(), 25, 25);
      super.paintComponent(g);
    }

    @Override
      protected void paintBorder(Graphics g) {
      if (getModel().isRollover()) {
        Graphics2D g2d = (Graphics2D) g;
        g2d.setColor(Color.MAGENTA);
        g2d.setStroke(new BasicStroke(3));
        g2d.drawRoundRect(0, 0, getWidth() - 1, getHeight() - 1, 25, 25);
      }
    }

    @Override
      public boolean contains(int x, int y) {
      if (shape == null || !shape.getBounds().equals(getBounds())) {
        shape = new RoundRectangle2D.Float(0, 0, getWidth(), getHeight(), 25, 25);
      }
      return shape.contains(x, y);
    }
  };
  btn.setOpaque(false);
  btn.setBorderPainted(false);
  btn.setContentAreaFilled(false);
  btn.setBackground(bg);
  btn.setForeground(fg);
  btn.setSize(w, h);
  btn.addActionListener(a);
  return btn;
}

void buildMenuBar(JFrame frame) {
  Dimension d = frame.getSize();
  JMenuBar bar = new JMenuBar();
  bar.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2));
  bar.setBackground(Color.DARK_GRAY);
  bar.add(createButton("Add", 80, 30, Color.yellow, Color.black, new ActionListener() {
    public void actionPerformed(ActionEvent actionEvent) {
      buttonPressed(((JButton)actionEvent.getSource()).getText());
    }
  }
  ));
  bar.add(createButton("Save", 80, 30, Color.green, Color.black, new ActionListener() {
    public void actionPerformed(ActionEvent actionEvent) {
      buttonPressed(((JButton)actionEvent.getSource()).getText());
    }
  }
  ));
  bar.add(createButton("Clear", 80, 30, Color.red, Color.black, new ActionListener() {
    public void actionPerformed(ActionEvent actionEvent) {
      buttonPressed(((JButton)actionEvent.getSource()).getText());
    }
  }
  ));
  bar.add(createButton("Open", 80, 30, Color.white, Color.black, new ActionListener() {
    public void actionPerformed(ActionEvent actionEvent) {
      buttonPressed(((JButton)actionEvent.getSource()).getText());
    }
  }
  ));
  frame.setJMenuBar(bar);
  frame.pack();
  frame.setSize(new Dimension((int)d.getWidth(), (int)(d.getHeight() + bar.getPreferredSize().getHeight())));
  frame.validate();
}

String recentButton = "";

void setup() {
  size(500, 500);
  textSize(50);
  textAlign(CENTER, CENTER);
  surface.setTitle("individual Menu using Swing components");
  buildMenuBar((JFrame) ((processing.awt.PSurfaceAWT.SmoothCanvas) surface.getNative()).getFrame());
}

void buttonPressed(String id) {
  println(id + " button pressed");
  recentButton = id;
}

void draw() {
  background(0);
  stroke(255, 0, 0);
  noFill();
  rect(0, 0, width-1, height-1);
  fill(255);
  text(recentButton, width/2, height/2);
}

```
