An undo and redo button for my drawing program

I have seen other peoples code of how to do it but I can’t seem to fully understand how it works. Especially when they have arrays and stuff. Someone please explain to me.

Here is a sketch with two different buttons. I have added click functionality. This is set to true when mouse is pressed.

The functionality you add to that is up to you.

Button b;
Button2 b1;

void setup(){
  size(400,400);
  b = new Button(20,20,"Test");
  b1 = new Button2(60,20,90,30,"Test");
};

void draw(){
  b.display();
  if(b.click())println(b.label);
  b1.display();
  if(b1.click())println(b1.label);
};

class Button {

  PVector pos;
  String label;
  int radius = 20;
  boolean mdown,toggle,click,m2down;

  Button(float _x, float _y, String _label) {
    pos = new PVector(_x, _y);
    label = _label;
  }

  void display() {
    if (pos()) fill(220);
    else noFill();
    stroke(150);
    ellipse(pos.x, pos.y, radius*2, radius*2);
    fill(150);
    text(label, pos.x-13, pos.y+4);
  }

  void toggle() {
    
    if (pos()&&mousePressed&&!mdown)mdown = true;
    if(!mdown&&!toggle){
      
      toggle = true;
      m2down = true;
    }
    if(!mdown&&toggle){
      toggle = false;
      m2down = true;
    }
    
    if(!mousePressed){
      mdown = false;
      m2down = false;
    }
    
  };
  
  boolean click(){
      boolean k = false;
      if (pos()&&mousePressed&&!click){
        click = true;
        k = true;
      }
      if(!mousePressed)click = false;
      
      return k;
  };
  
  boolean pos(){
    PVector mouse = new PVector(mouseX, mouseY);
    return mouse.dist(pos) < radius;
  };
  
  boolean hold(){
    if(mdown)return true;
    else return false;
      
  };
  
};

class Button2{
  
  float x,y,w,h;
  boolean mclick,mdown,click;
  String label;
  
  Button2(){
    
  };
  
  Button2(float x,float y,float w,float h,String label){
    
    this.x = x;
    this.y = y;
    this.w = w;
    this.h = h;
    this.label = label;
    
    if(this.w<textWidth(label)+5)this.w = textWidth(label)+5;
    
  };
  
  void display(){
    
    fill(255);
    if(pos())fill(200);
    stroke(200);
    strokeWeight(1);
    noStroke();
    rect(x,y,w,h);
    fill(50);
    text(label,x+2,y+14);
    
    //logic();
    //click = false;
    
  };
  
  boolean click(){
      boolean k = false;
      if (pos()&&mousePressed&&!click){
        click = true;
        k = true;
      }
      if(!mousePressed)click = false;
      
      return k;
  };
  
  boolean mouseDown(){
    boolean k = false;
    if(pos()&&mousePressed)k = true;
    if(!mousePressed)k = false;
    return k;
  };
  
  
  boolean pos(){
    return mouseX>x&&mouseX<x+w&&mouseY>y&&mouseY<y+h;
  };
  
};

seperate button classes
button 1;

class Button {

  PVector pos;
  String label;
  int radius = 20;
  boolean mdown,toggle,click,m2down;

  Button(float _x, float _y, String _label) {
    pos = new PVector(_x, _y);
    label = _label;
  }

  void display() {
    if (pos()) fill(220);
    else noFill();
    stroke(150);
    ellipse(pos.x, pos.y, radius*2, radius*2);
    fill(150);
    text(label, pos.x-13, pos.y+4);
  }

  void toggle() {
    
    if (pos()&&mousePressed&&!mdown)mdown = true;
    if(!mdown&&!toggle){
      
      toggle = true;
      m2down = true;
    }
    if(!mdown&&toggle){
      toggle = false;
      m2down = true;
    }
    
    if(!mousePressed){
      mdown = false;
      m2down = false;
    }
    
  };
  
  boolean click(){
      boolean k = false;
      if (pos()&&mousePressed&&!click){
        click = true;
        k = true;
      }
      if(!mousePressed)click = false;
      
      return k;
  };
  
  boolean pos(){
    PVector mouse = new PVector(mouseX, mouseY);
    return mouse.dist(pos) < radius;
  };
  
  boolean hold(){
    if(mdown)return true;
    else return false;
      
  };
  
};

button 2

class Button2{
  
  float x,y,w,h;
  boolean mclick,mdown,click;
  String label;
  
  Button2(){
    
  };
  
  Button2(float x,float y,float w,float h,String label){
    
    this.x = x;
    this.y = y;
    this.w = w;
    this.h = h;
    this.label = label;
    
    if(this.w<textWidth(label)+5)this.w = textWidth(label)+5;
    
  };
  
  void display(){
    
    fill(255);
    if(pos())fill(200);
    stroke(200);
    strokeWeight(1);
    noStroke();
    rect(x,y,w,h);
    fill(50);
    text(label,x+2,y+14);
    
    //logic();
    //click = false;
    
  };
  
  boolean click(){
      boolean k = false;
      if (pos()&&mousePressed&&!click){
        click = true;
        k = true;
      }
      if(!mousePressed)click = false;
      
      return k;
  };
  
  boolean mouseDown(){
    boolean k = false;
    if(pos()&&mousePressed)k = true;
    if(!mousePressed)k = false;
    return k;
  };
  
  
  boolean pos(){
    return mouseX>x&&mouseX<x+w&&mouseY>y&&mouseY<y+h;
  };
  
};

Alternatively feel free to use controlp5 or g4p

Hello!
Are you talking about a button that functions like an on-off switch?

Can you explain a bit of the logic behind this code? I still don’t quite understand

Are you interested in

  • how a button (for the mouse) might work (no matter its function) (see code by paulgoux) OR
  • how to do undo and redo? (Even with keyboard)

The latter depends very strong on how your drawing Sketch stores the shapes.

  • Do you just put new shapes on the canvas (without using background() in draw())? Then you could store every image/frame (before a change is drawn) in an array using get() and use undo in that array OR
  • do you have all shapes in an ArrayList? Then this Sketch is useful for you.

Sketch for an ArrayList

ArrayList<PVector> list = new ArrayList(); 

void setup() {
  size(400, 400);
}

void draw() {
  background(0); 

  text("click mouse, Backspace to undo", 17, 17);

  for (PVector pv : list)
    ellipse(pv.x, pv.y, 
      19, 19);
}

void mousePressed() {
  list.add(new PVector(mouseX, mouseY));
}

void keyPressed() {
  if (key==BACKSPACE) 
    if (list.size()>0)
      list.remove(list.size()-1);
}

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

What part don’t you understand? To explain the entire Sketch is a bit much to ask.

Explanation

The Sketch by paulgoux just shows 2 buttons without undo/redo. (The }; is the same as } by the way.)

It uses objects, are you familiar with this paradigm of OOP?

see Objects \ Processing.org

We have 2 different classes and 2 buttons derived from them.

Hey, and welcome to the forum! Great to have you here!

Warm regards,

Chrisir :wink:

Thanks for the welcome! Tbh I feel like these codes are above my level in several ways as I don’t understand too much of them (especially arrays). I will probably just try to learn a bit more about the codes in processing before I attempt to do this.

1 Like

a slightly simpler sketch it holds only two states. It outputs a toggle boolean. Its up to you to add conditions and code based on the state of the toggle which can be either true or false;

boolean click, toggle;

void setup() {
  size(600,600);
};

void draw() {
  background(50);
  fill(255);
  rect(200, 200, 200, 50);
  click();
};
//check if mouse has been clicked
boolean click() {
  boolean k = false;
  if (pos()&&mousePressed&&!click) {
    click = true;
    
  } else if (click&&!mousePressed) {
    k = true;
    click = false;
  }
//this now assigns your toggle variable
  if (k&&toggle)toggle = false;
  else if (k&&!toggle)toggle = true;
  if (k)println("toggle", toggle);
  return k;
};
// this checks the location of the mouse 
boolean pos() {
  return mouseX>200&&mouseX<200+400&&mouseY>200&&mouseY<200+50;
};

simpler still, this makes use of processing’s own mousePressed function

boolean click, toggle;

void setup() {
  size(600,600);
};

void draw() {
  background(50);
  fill(255);
  if(toggle)fill(255,50);
  rect(200, 200, 200, 50);
};

void mousePressed(){
  if(pos()){
    if(toggle)toggle = false;
    else toggle = true;
    println("toggle", toggle);
  }
};

boolean pos() {
  return mouseX>200&&mouseX<200+400&&mouseY>200&&mouseY<200+50;
};

You don’t need ; after }

Hello,

Learning is the fun part!

There are lots of resources (tutorials, references, examples, etc.) here:
processing.org

Check out the examples that come with Processing.
These are in the menu under File > Examples…> :

image

Also check out YouTube for the Coding Train.

There are arrays and ArrayLists:

Example of mouse movement displaying elements of array:

// Array Example
// v1.0.0
// GLV 2021-05-21

// Declare arrays for x, y; can be replace with PVectors later!
int x[] = new int[100];
int y[] = new int[100];

//Runs once
void setup() 
  {
  size(500, 500);
  
  // Shows initial values of array... all 0
  //printArray(x); 
  //printArray(y);
  
  //Initialize values of array with random values
  for(int i = 0; i<100; i++)
    {
    x[i] = int (random(width));
    y[i] = int (random(height));
    }
  
  // Shows values of array in console
  //printArray(x);
  //printArray(y);    
  }

//Loops at a rate of 60 frames per sec
void draw() 
  {
  background(0);
  strokeWeight(2);
  stroke(255, 255, 0);
  
  int max = mouseX/5; // Also see map();
  
  println(mouseX, max); // Shows values in console
  
  for(int i = 0; i<max; i++)
  //for(int i = 0; i<100; i++)
    {
    point(x[i], y[i]);
    }  
 }

I tried to keep it simple.

Lookup all the references such as setup(), draw(), random() and so on to fully understand them.

The various print() functions can be very helpful in coding and I use these often in development.

:)

Hello Paul

I have a question to a part of your code. The way I try to make sense of it confuses myself :wink:

void mousePressed(){
  if(pos()){ // if the boolean "pos" is true then execute the following?
    if(toggle)toggle = false; // if the boolean "toggle" is true then set it to false? would seem a little strange to me, but don't have any better idea...
    else toggle = true;
    println("toggle", toggle);
  }
};

Thanks for your explanation!

1 Like

Yeah, toggle means switch it to the other value it can have.

On becomes off and vice versa

Short form: toggle = ! toggle;

You have to think about what you are trying to achieve, and what the state of the bool is after each click.

Button is off
If click
I want button on

Now we can stop there and we could use this to trigger an event or function.

my code takes it one step further and considers what if our program does more than just set this button on.
Or what if we want to be able to set it again later.

So do we want the button to automatically switch back to off without user input, say based on another condition, such as another bool state or an elapsed time.

Again you can choose to do nothing, or you can change it back using another condition.

In this case the condition is another button press.

Button is on
If click
I want button off

Ok. Thanks!
Just needed some time to wrap my head around this concept ^^

I’m wondering how I can store images before and after a change has been made to the drawing and call it by either tapping undo(deletes the previous action) or redo which restores the thing previously undone using undo button

1 Like


color[] c = new color[3];
int x;
int i = 40; 
int index; 

PImage[] listForUndo = new PImage[0]; 

void setup() {
  size(1400, 400);

  c[0]=color(255, 0, 0); // first color
  c[1]=color(0, 255, 0); // second
  c[2]=color(0, 0, 255); // third

  listForUndo = (PImage[]) append(listForUndo, get() );   // append
  index++;
}

void draw() {
  //
}//func

void keyPressed() {

  if (key==BACKSPACE) {
    //UNDO
    index--;
    if (index<0)
      index=0; 
    image(listForUndo[index], 0, 0);
    return;
  }

  if (keyCode==RIGHT) {
    //REDO
    index++; 
    if (index>=listForUndo.length-1)
      index=listForUndo.length-1; 
    image(listForUndo[index], 0, 0);
    return;
  }

  if (key==' ') {

    fill(c[x]);
    ellipse(i, 100, 30, 80);
    i+=80;

    listForUndo = (PImage[]) append(listForUndo, get() );   // append
    index++; 

    x = x + 1;
    if (x>2) { 
      x=0;
    }
    return;
  }
}
//