如何单独更改每个 controlP5 下拉列表条目的颜色?

How to change color of each controlP5 dropdown list entry individually?

使用以下 MWE:

import controlP5.*;
import java.util.*;


ControlP5 cp5;

void setup() {
  size(400, 400);
  cp5 = new ControlP5(this);
  List l = Arrays.asList("a", "b", "c", "d", "e", "f", "g", "h");
  /* add a ScrollableList, by default it behaves like a DropdownList */
  cp5.addScrollableList("dropdown")
     .setPosition(100, 100)
     .setSize(200, 100)
     .setBarHeight(20)
     .setItemHeight(20)
     .addItems(l)
     // .setType(ScrollableList.LIST) // currently supported DROPDOWN and LIST
     ;
     
     
}

void draw() {
  background(240);
}

如何分别更改每个列表条目的字体颜色?我假设我必须使用类似下面的某种 for 循环

    for (int i =0; i < myarray.length; i++){
      dropdown.setColorActive(elementDetermine(myarray[i]));
    }

其中 elementDetermine() 接受一个整数和 returns 一种颜色。不幸的是,当我 运行 在设置或绘图函数中循环时,下拉列表不会在没有错误消息的情况下发生变化。

遗憾的是,没有简单易行的方法来单独更改每个列表条目的字体颜色。

如您在 documentation 中所见,可用方法访问适用于所有项目的主标题。

事实上,相同的 Label 组件在所有列表项中 re-used 并且每个项仅 re-rendered 一次(更改当前 text/colours),如您所见source code

如果你真的真的需要改变字体颜色,你可以,但是要跳过很多 OOP 环节(因为每个项目没有 Label 个实例的数组):

  • 您需要自定义 ControllerView< ScrollableList > 来覆盖它的 display() 方法
  • 为了使自定义 display() 与原始 ScrollableList 一样工作,您需要几乎复制超级 class
  • ScrollableList super class 有一堆私有属性可以很容易地从 ScrollableListView class 访问,因为它是 ScrollableList 的一部分,但是在我们的例子中,所有需要在 display() 中访问的 private / protected 属性都需要可访问。为此,实现了 ScrollableList subclass (PopUpScrollableList),它主要复制它的 superclass 行为,无论这些 private/protected 属性在哪里在 display() 中使用并使它们可用。

最后,自定义可滚动列表可以有一个 color[],它可以存储自定义颜色,以便可以在覆盖的 display() 方法中访问和呈现它们:

import controlP5.*;
// used by the custom scrollable list view
import static controlP5.ControlP5.b;

import java.util.*;


ControlP5 cp5;


void setup() {
  size(400, 400);
  cp5 = new ControlP5(this);
  List l = Arrays.asList("a", "b", "c", "d", "e", "f", "g", "h");
  
  /* add a custom ScrollableList, by default it behaves like a DropdownList */
  PopUpScrollableList dropdown = addPopUpScrollableList("dropdown");
  
  dropdown.setPosition(100, 100)
     .setSize(200, 100)
     .setBarHeight(20)
     .setItemHeight(20)
     .addItems(l)
     // .setType(ScrollableList.LIST) // currently supported DROPDOWN and LIST
     ;
  
  // a list of (random) colourr: a colour per item
  color[] textColors = new color[l.size()];
  for(int i = 0 ; i < textColors.length; i++){
    textColors[i] = color(255, random(255), random(255), random(255));
  }
  // set custom text colours: this is a bit hacky: normally you'd check if items.size() matches textColors.length, etc. 
  dropdown.textColors = textColors;
  
  // set a custom view for the list
  dropdown.setView(makeCustomScrollListView());
}

void draw() {
  background(240);
}

// simply adds our custom scrollable list
PopUpScrollableList addPopUpScrollableList( final String theName ) {
  PopUpScrollableList myController = new PopUpScrollableList(cp5, theName);
  return myController;
}

ControllerView< ScrollableList > makeCustomScrollListView(){
  // make an custom scrollable list view on the fly and access the fileds it needs
  return new ControllerView< ScrollableList >() {
      
          // tweaked version of https://github.com/sojamo/controlp5/blob/1f7cb649865eb8657495b5cfeddd0dbe85d70cac/src/controlP5/ScrollableList.java#L391
          public void display( PGraphics g , ScrollableList c ) {

            // setHeight( -200 ); /* UP */
            PopUpScrollableList d = (PopUpScrollableList)c;
            g.noStroke( );
            
            if ( c.isBarVisible( ) ) {
              boolean b = d.itemHover() == -1 && d.isInside() && !d.isDragged();
              g.fill( b ? c.getColor( ).getForeground( ) : c.getColor( ).getBackground( ) );
              g.rect( 0 , 0 , c.getWidth( ) , c.getBarHeight() );
              g.pushMatrix( );
              g.translate( c.getWidth( ) - 8 , c.getBarHeight() / 2 - 2 );
              g.fill( c.getColor( ).getCaptionLabel( ) );
              if ( c.isOpen( ) ) {
                g.triangle( -3 , 0 , 3 , 0 , 0 , 3 );
              } else {
                g.triangle( -3 , 3 , 3 , 3 , 0 , 0 );
              }
              g.popMatrix( );
      
              c.getCaptionLabel( ).draw( g , 4 , c.getBarHeight() / 2 );
      
            }
      
            if ( c.isOpen( ) ) {
              int bar = ( c.isBarVisible( ) ? c.getBarHeight() : 0 );
              int h = ( ( d.updateHeight( ) ) );
              g.pushMatrix( );
              // g.translate( 0 , - ( h + bar +
              // c.itemSpacing ) ); /* UP */
              g.fill( c.getBackgroundColor( ) );
              g.rect( 0 , bar , c.getWidth( ) , h );
              g.pushMatrix( );
              g.translate( 0 , ( bar == 0 ? 0 : ( c.getBarHeight() + d.itemSpacing() ) ) );
              /* draw visible items */
              c.updateItemIndexOffset( );
              int m0 = d.itemIndexOffset;
              
              List items = c.getItems();
              
              int m1 = items.size( ) > d.itemRange() ? ( d.itemIndexOffset + d.itemRange() ) : items.size( );
              for ( int i = m0 ; i < m1 ; i++ ) {
                Map< String , Object > item = (Map< String , Object >) items.get( i );
                CColor itemColor = ( CColor ) item.get( "color" );
                g.fill( ( b( item.get( "state" ) ) ) ? itemColor.getActive( ) : ( i == d.itemHover() ) ? ( c.isMousePressed() ? itemColor.getActive( ) : itemColor.getForeground( ) ) : itemColor.getBackground( ) );
                
                float boxY = d.itemHeight() - 1;
                
                g.rect( 0 , 0 , c.getWidth( ) , boxY );
                Label label = c.getValueLabel( );
                
                // finally set custom text colour
                if(d.textColors != null){ 
                  label.setColor(d.textColors[i]);
                }
                
                label.set( item.get( "text" ).toString( ) ).draw( g , 4 , d.itemHeight() / 2 );
                g.translate( 0 , d.itemHeight() );
              }
              g.popMatrix( );
      
              if ( c.isInside() ) {
                int m = items.size( ) - d.itemRange();
                if ( m > 0 ) {
                  g.fill( c.getColor( ).getCaptionLabel( ) );
                  g.pushMatrix( );
                  int s = 4; /* spacing */
                  int s2 = s / 2;
                  g.translate( c.getWidth( ) - s , c.getBarHeight() );
                  int len = ( int ) PApplet.map( ( float ) Math.log( m * 10 ) , 0 , 10 , h , 0 );
                  int pos = ( int ) ( PApplet.map( d.itemIndexOffset , 0 , m , s2 , h - len - s2 ) );
                  g.rect( 0 , pos , s2 , len );
                  g.popMatrix( );
                }
              }
              g.popMatrix( );
            }
      
          }

  };
}


// a custom ScrollableList subclass: mainly it needs to expose properties the custom view (via setView()) can't access in display()
class PopUpScrollableList extends ScrollableList{
  
  private int itemIndexOffset = 0;
  private int _myType = DROPDOWN;
  protected boolean isOpen = true;
  
  public color[] textColors;
  
  PopUpScrollableList(ControlP5 cp5, String name){
    super(cp5, name);
    println("custom PopUpScrollableList named " + name + " constructed");
  }
 
  public boolean isOpen( ) {
    return isOpen;
  }

  public ScrollableList open( ) {
    return setOpen( true );
  }

  public ScrollableList close( ) {
    return setOpen( false );
  }

  public ScrollableList setOpen( boolean b ) {
    isOpen = b;
    return this;
  }
 
  public int itemHover(){
    return itemHover;
  }
  
  public int itemSpacing(){
    return 1;
  }
  
  public int itemRange(){
    return itemRange;
  }
  
  public int itemHeight(){
    return itemHeight;
  }
  
  public boolean isInside(){
    return isInside;
  }
  
  public boolean isDragged(){
    return isDragged;
  }
  
  public int barHeight(){
    return barHeight;
  }
  
  protected int updateHeight( ) {
    itemRange = ( PApplet.abs( getHeight( ) ) - ( isBarVisible( ) ? barHeight : 0 ) ) / itemHeight;
    return itemHeight * ( items.size( ) < itemRange ? items.size( ) : itemRange );
  }
  
  public List< Map< String , Object > > items(){
    return items;
  }
  
  @Override protected void onRelease( ) {
    if ( !isDragged ) {
      if ( getPointer( ).y( ) >= 0 && getPointer( ).y( ) <= barHeight ) {
        setOpen( !isOpen( ) );
      } else if ( isOpen ) {

        double n = Math.floor( ( getPointer( ).y( ) - barHeight ) / itemHeight );

        // n += itemRange; /* UP */
        int index = ( int ) n + itemIndexOffset;
        updateIndex( index );
      }
    }
  }

  private void updateIndex( int theIndex ) {
    if ( theIndex >= items.size( ) ) {
      return;
    }

    Map m = items.get( theIndex );

    switch ( _myType ) {
    case ( LIST ):
      super.setValue( theIndex );
      for ( Object o : items ) {
        ( ( Map ) o ).put( "state" , false );
      }
      m.put( "state" , !ControlP5.b( m.get( "state" ) ) );
      break;
    case ( DROPDOWN ):
      super.setValue( theIndex );
      setOpen( false );
      getCaptionLabel( ).setText( ( m.get( "text" ).toString( ) ) );
      break;
    case ( CHECKBOX ):
      m.put( "state" , !ControlP5.b( m.get( "state" ) ) );
      break;
    }

  }

  public ScrollableList setValue( float theValue ) {
    updateIndex( ( int ) ( theValue ) );
    return this;
  }
  
  @Override protected void onDrag( ) {
    scroll( getPointer( ).dy( ) );
  }

  @Override protected void onScroll( int theValue ) {
    scroll( theValue );
  }

  private void scroll( int theValue ) {
    if ( isOpen() ) {
      itemIndexOffset += theValue;
      itemIndexOffset = ( int ) ( Math.floor( Math.max( 0 , Math.min( itemIndexOffset , items.size( ) - itemRange ) ) ) );
      itemHover = -2;
    }
  }
  
  @Override protected void onLeave( ) {
    itemHover = -1;
  }
  
  @Override protected void onEnter( ) {
    updateHover( );
  }

  @Override protected void onMove( ) {
    updateHover( );
  }

  @Override protected void onEndDrag( ) {
    updateHover( );
  }
  
  private void updateHover( ) {
    if ( getPointer( ).y( ) > barHeight ) {
      double n = Math.floor( ( getPointer( ).y( ) - barHeight ) / itemHeight );
      itemHover = ( int ) ( itemIndexOffset + n );
    } else {
      itemHover = -1;
    }
  }
  
  public void updateItemIndexOffset( ) {
    int m1 = items.size( ) > itemRange ? ( itemIndexOffset + itemRange ) : items.size( );
    int n = ( m1 - items.size( ) );
    if ( n >= 0 ) {
      itemIndexOffset -= n;
    }
  }
  
  
}