Self rendering objects?


#1

Hello everybody!

I’m working on my own GUI project. Can someone point me on the right direction to make “self rendering objects”?

For example, ive got a class Buttons, i would like to make an instance of that class and the button render on the screen each frame without me having to call a MyButton.Render() function.

Thank you all.


#2

The following code shows how an object can ‘self-register’ to draw itself. In a GUI you will also need to register for mouse and key events more info can be found here.

Button b;

void setup() {
  size(300, 200);
  b = new Button(this, 30, 20, 200, 30);
}

void draw() {
  background(255, 255, 200);
}

public class Button {

  PApplet pa;
  int x, y, w, h;

  public Button(PApplet pa, int x, int y, int w, int h) {
    this.pa = pa;
    this.x = x;
    this.y = y;
    this.w = w;
    this.h = h; 
    pa.registerMethod("draw", this);
  }

  public void draw() {
    pa.pushMatrix();
    pa.pushStyle();
    pa.fill(180, 180, 255);
    pa.stroke(0, 0, 128);
    pa.strokeWeight(2);
    pa.translate(x, y);
    pa.rect(0, 0, w, h);
    pa.popStyle();
    pa.popMatrix();
  }
}

Processing already has some GUI libraries you might look at G4P and ControlP5 :grinning:


#3

Hello quark!

Thank you for your reply! This will very helpfull.

Yes, i know there are some GUI libraries already, in fact, i’ve been using G4P for a long time and i love it, but i just wanted to do my own modest version :slight_smile:

Thank you again


#4

One thing to think hard about when developing a collection of self-rendering objects is whether you want them to be responsible for their own positions, or whether you want a container (or the sketch) to manage positioning them.

It is often the case that, reflexively, one may start passing lots of xywh around everywhere for everything. That isn’t always necessary.

For example, in the Processing, rect() is xywh, while box() and sphere do not have an xyz – you position them with translate() calls. Both methods are effective, but different. In UI, an interesting example might be an accordion or drop-down menu items – you might want them to have relative offsets, or just indexes and no positions at all.


#5

For me it is an easy choice because good OO (object orientation) design requires classes to be autonomous so a self-rendering object (i.e. a GUI control) must be responsible for its own position, size, colour etc. The trick here is to realise that the position of the object need not be the actual screen position but relative to some parent control.

In G4P all controls have their own [x,y] position attributes / fields.

G4P has the GPanel class which implements a floating panel to which any other G4P control can be added. In this case the panel position [x,y] holds the actual screen position and the [x,y] fields of any added control are relative to the panels position.

To enable this the GPanel class maintains a list of other controls that have been added to it.

The scrollbars (GSlider class) used in GTextArea, GTextField and GDropList work in a similar manner.


#6

Right, it depends on your goals: if you want your objects to be autonomously self-rendering, they should have what they need.

An interesting recent counter-use-case was this animated text stream.

OP set out to make the letter autonomously renderable, but that was needlessly complex – in fact, they were always being rendered relative to an accumulation of shifting previous objects, so one alternate approach that made sense was for them not to store their own location, but instead be positioned in a typing stream.

Of course, that approach makes more sense if you are changing the locations of every object every frame or constantly re-sequencing – not common for most GUI control elements.


#7

I saw the discussion about animated text and what you say is reasonable.

In this case I would have a class called AnimatedText which would have a private inner class to represent a single character lets call it Character so can only be accessed by the enclosing class AnimatedText.

So where do we store the display coordinates for individual characters?
Two options

  1. Inside the Character class and updated by the AnimatedText class.
  2. Inside and updated by the AnimatedText class as a collection.

For me option (1) is the correct choice.

Why not option (2), because it requires two collections one for coordinates and one for characters and they must synchronised / linked to maintain the position <> character link.

Option (1) requires just one collection so easily enables lots of clever text manipulation e.g. inserting / deleting / reordering characters etc.


#8

@quark i like your example and used it, until i got stuck:

as a not “self rendering” i would have to call the .draw methode manually,
( and when ever I WANT. )
with this one i not need, it draws always
and that’s the point, it must have a show & hide methode add to the
auto draw.

could you pls help to add this for this example?


#9

I have modified the sketch so the self-rendering object can be invisible. The ‘v’ key toggles visibility.

Button b;

void setup() {
  size(300, 200);
  b = new Button(this, 30, 20, 200, 30);
}

void draw() {
  background(255, 255, 200);
  fill(0);
  textSize(16);
  text("Hit 'v' to make visible / invisible", 30, 100);
}

void keyReleased() {
  if (key == 'v') {
    b.setVisible(!b.isVisible());
  }
}

public class Button {

  PApplet pa;
  int x, y, w, h;
  boolean visible = true;

  public Button(PApplet pa, int x, int y, int w, int h) {
    this.pa = pa;
    this.x = x;
    this.y = y;
    this.w = w;
    this.h = h; 
    pa.registerMethod("draw", this);
  }

  public void setVisible(boolean visible) {
    this.visible = visible;
  }

  public boolean isVisible() {
    return visible;
  }

  public void draw() {
    if (visible) {
      pa.pushMatrix();
      pa.pushStyle();
      pa.fill(180, 180, 255);
      pa.stroke(0, 0, 128);
      pa.strokeWeight(2);
      pa.translate(x, y);
      pa.rect(0, 0, w, h);
      pa.popStyle();
      pa.popMatrix();
    }
  }
}

#10

now it looks so easy… perfect,
i was thinking there is a “unregistering” of the draw needed.