Looking for clip(), noClip() alternative; it no longer works

I have used the clip(), noClip() pair to constrain scrolling text to a rectangle in Android 4.4.2.
The same sketch does not work in Android 12 and maybe prior to that. It seems it has been deprecated.
I have been unable to produce the same effect with any other code. As the text scrolls it becomes visible above and below the rectangle. Simply placing a rectangle to cover that excess text is not acceptable.
I have produced fixed size text pages in the rectangle, which works but requires the text not to change or the paging calculation becomes impractical.
Libraries such as G4P and P5 do not seem useable for Android.
Can anyone suggest a solution please.

If your clip area is a simple rectangle, you can render it to a PGraphics and image() it to the screen.

@scudly

Thank you for the response. I am not familiar with using those, so before I look into that will it permit scrolling the text?

String s = "This is some text that scrolls.    ";
float w;
PGraphics pg;

void setup() {
  size( 600, 400 );
  pg = createGraphics( 200, 100 );
  pg.beginDraw();
  pg.textSize( 36 );
  w = pg.textWidth( s );
  pg.endDraw();
  s = s + s;   // double the string so that it wraps
}

void draw() {
  background( 64 );
  pg.beginDraw();
  pg.background(0);
  pg.text( s, -(frameCount%w), 64 );
  pg.endDraw();
  image( pg, 100, 100 );
}

Thank you scudly. I realise only now that I was unclear and ambiguous. I should have described the scrolling better, I never thought of horizontal. The scrolling I need is manual, vertical. A help file vertically scrolling up and down with mouse clicking up/down buttons on a bar at the top.
The file being a simple text file which is already treated for the width of the box. My original was as follows (which clearly cannot work here). DPane is simply a label used for many sub classes and uses… registerMethod(ā€œdrawā€,this); within it.
The box size is fixed by other factors and the bar with buttons has to be within this box, not above it. This is part of a very much bigger sketch. It works perfectly on KitKat.
I need to replace the clip() noClip() within draw(). Removing those two lines shows the text above and below the rectangle as it scrolls.

public class DTextarea extends DPane {
  DBar bar;
  int stop, move=0, pad=4;
  int barClr, bar_h;
  PFont bar_font;
  String caption="";    
  float textHeight,myTemp; 
  
  DTextarea(int _x, int _y, int _w, int _h,int _labClr, PFont _font, int _barClr, int _bar_h, PFont _bar_font, int _btnCloseClr){
  super(_x, _y, _w, _h, _labClr, _font);
  barClr=_barClr; bar_h=_bar_h; bar_font=_bar_font;
  
  bar=new DBar(x, y, w, bar_h, barClr, bar_font, _btnCloseClr);
  textFont(font);
//  textHeight=textAscent()+textDescent()+2;
//  stop=lineCount * int(textHeight) * -1 +h -bar_h; 
  }
//--------------------------------------------------------------  
  public void setCaption(String _caption, int _capClr ){
  caption=_caption; capClr=_capClr;
  }
//--------------------------------------------------------------
 void draw(){
    if (visible) {
      super.draw();
      fill(capClr);
      textFont(font);
      textLeading(textHeight);
      textAlign(LEFT,CENTER);
      clip(x,y+bar_h,w,h-bar_h);
      pushMatrix();
      translate(0, move);
      text (caption, x+pad, y+bar_h, w-pad, lineCount * int(textHeight));
      popMatrix();
      noClip();
    }//visible?
  }//draw

  public void setVisible(boolean visible){
    move=0;
    textHeight=textAscent()+textDescent()+2;
    stop=(lineCount * int(textHeight)-400) *-1;    
    super.setVisible(visible);
    this.visible = visible;
    bar.setVisible(visible);
  }
  boolean getVisible() {return visible;}
//--------------------------------------------------------------
  boolean mouseIsOver(){
    return (visible && mouseX>x &&  mouseX< x+w
      && mouseY >y && mouseY< y+h);
  }
//--------------------------------------------------------------
  public void mousePressed(){
    if (bar.btnClose.mouseIsOver()){
      setVisible(false);
      bar.btnDn.setVisible(false);
      bar.btnUp.setVisible(false);
      setCornersEnabled(true);
    }   
    if (mouseIsOver()){
      myTemp=mouseY;
   }
 

//--------------------------------------------------------------
  if (bar.btnUp.mouseIsOver()) {
    bar.btnDn.setVisible(true);
      move=move+5*int(textHeight);
      if(move>0){
        move=0;
        bar.btnUp.setVisible(false);
      }
  }  
//--------------------------------------------------------------
  if (bar.btnDn.mouseIsOver()) {
    bar.btnUp.setVisible(true);
      move=move-5*int(textHeight);
      if (move<stop) {
        move=stop;
        bar.btnDn.setVisible(false);
      }
  }
//-------------------------------------------------------------
The following was called to prepare the text.
//SIZETEXT------------------------------------------------------
  void sizeText(String[] paras, int boxWidth) {
    textFont(arial16);
    String[] words=new String[0];
    String pre="", thisLine="";
    float w=0;
//For each para    
    for (int p=0; p<paras.length; p++){    
      words = split(paras[p]," ");
      for (int i=0; i<words.length; i++){
//Does it fit?
          w =w + textWidth(pre+words[i]);
          if (w<boxWidth){
//Yes it fits
            thisLine=thisLine+pre+words[i]; 
            pre=" ";
//Last word of para?
            if (i==words.length-1){
              thisLine=thisLine + "\n";
              helpText=helpText + thisLine;
              lineCount++;
              thisLine="";
              pre="";
              w=0;
              break;
            }//last word? 
//Does not fit
          }else {
           thisLine=thisLine + "\n";
           helpText=helpText + thisLine;
           lineCount++;
          thisLine="";
          pre="";
          w=0;
          i=i-1;
          }
      }//for i
    }//Next para
  }//sizeText 

Is there an android widget that could do what you want, eg How to Make TextView Scrollable in Android? | GeeksforGeeks. I was unable to run your last demo.

Here’s an example of a scrollable textView:

import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.widget.TextView;
import android.R;
import android.view.ViewGroup.LayoutParams;
import android.widget.FrameLayout;
import android.widget.RelativeLayout;
import android.text.method.ScrollingMovementMethod;

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(32, 175, 47);
color ORANGE = color(237, 147, 29);

Activity activity;
Context ctx;

void setup() {
  fullScreen();
  orientation(LANDSCAPE);
  background(BLUE);
  activity = this.getActivity();
  ctx = activity.getApplicationContext();
  runOnUiThread(new Runnable() {
    void run() {
      textView(370, 510, 600, 110, WHITE, 26.0);  
    }
  }
  );
}

void draw() {
  background(BLUE);
}

TextView code under separate tab:

void textView(int x, int y, int w, int h, color txtColor, float txtSize) {

  TextView textView = new TextView(ctx);
  textView.setLayoutParams (new RelativeLayout.LayoutParams (RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT));
  textView.setX(x);
  textView.setY(y);
  textView.setWidth(w);
  textView.setHeight(h);
  textView.setTextColor(txtColor);
  textView.setTextSize(txtSize);
  textView.setMovementMethod(new ScrollingMovementMethod());
  textView.setText("This is a bunch of text which goes on and on ... aaaa bbbbb cccccccccc dddddddddddddddddd eeeeeeeeeeeeeeeeeeeeeeee ffffffffffffffffffffff ggggggggg");
  FrameLayout flayout = (FrameLayout)activity.findViewById(R.id.content);
  flayout.addView(textView);
}

The code @svan suggests is probably the right solution if you are strictly on Android. And obviously clip() should still work as you originally had it, so if it doesn’t that sounds like a bug that you should report on github. That said, you should still be able to use the PGraphics to do what you want for drawing the scrollable text widget.

As a minimal test for clip(), maybe you and @svan could both try this code both with and without the clip() line to see if it works. It should behave exactly the same as my PGraphics-using code I posted above.

String s = "This is some text that scrolls.    ";
float w;

void setup() {
  size( 600, 400 );
  textSize( 36 );
  w = textWidth( s );
  s = s + s;   // double the string so that it wraps
}

void draw() {
  background( 64 );
  translate( 100, 100 );
  clip( 0, 0, 200, 100 );
  background(0);
  text( s, -(frameCount%w), 64 );
}

It works in Android mode as advertised both with and without ā€˜clip’. Horizontal scrolls (he may need vertical scroll). I tested with version 4.3 since Android mode appears broken on v 4.4.1. Nice demo.

@scudly
@svan
Thank you both for your time.
The problem with clip() comes when noClip() follows as in the code I posted and added the note that it cannot run here because it is part of a much bigger program.
My sketch has a button that when pressed displays the rectangle in the centre of the GUI and is filled with text (help file) which can be scrolled with a button. When the help text is dismissed the noClip() releases the clipped area back to normal. It is then that on Android 12 but not Android 4.4.2 that I get this message…

FATAL EXCEPTION: Animation Thread
Process: processing.test.sketch_250508b, PID: 29732
java.lang.IllegalArgumentException: Invalid Region.Op - only INTERSECT and DIFFERENCE are allowed
	at android.graphics.Canvas.checkValidClipOp(Canvas.java:821)
	at android.graphics.Canvas.clipRect(Canvas.java:960)
	at processing.a2d.PGraphicsAndroid2D.noClip(PGraphicsAndroid2D.java:539)
	at processing.core.PApplet.noClip(PApplet.java:8694)
	at processing.test.sketch_250508b.sketch_250508b.draw(sketch_250508b.java:36)
	at processing.core.PApplet.handleDraw(PApplet.java:1895)
	at processing.core.PSurfaceNone.callDraw(PSurfaceNone.java:479)
	at processing.core.PSurfaceNone$AnimationThread.run(PSurfaceNone.java:519)

Upon following that up on the internet it became clear, as I said, that clip() noClip() is deprecated with no replacement. So an alternative is required that permits the text in the ā€˜help’ box to be scrolled and clipped by the box. What it does if noClip() is removed is to show the scrolling text outside the box.
The box produced by the sketch (draw() that i showed, has another function when the ā€˜help’ is dismissed.; hence the noClip() and the push & popMatrix.
The text scrolling that works on 4.4.2 with clip() is exactly like the scrolling box that you use to post your responses on this forum, and that I am using right now.

As a general rule it’s better to post a minimal runnable example that we can all run. It’s very difficult to try and find the problem from code snippets.

1 Like

@svan
Noted, my mistake. Will do better next time :slight_smile:
If you take my last remark about the forum scrolling box; no example should be needed now.

So it sounds like you need to stop using clip() and use PGraphics instead as in my first example. It’s a little more awkward to prepend pg. in front of all of the drawing commands, but not too bad.

Just be warned that each PGraphics maintains its own drawing state entirely separate from the default canvas which means that you will need to set the font, text size, stroke color, etc. within it and any changes you make there have no effect on the default canvas.

@scudly
Thank you, it sounds much more difficult than clip(). I will experiment as you suggest.