如何让 ControlP5 Dropodownlist 打开而不是向下打开?
How to let ControlP5 Dropodownlist open up instead of down?
使用文档中的这个 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);
}
如何让下拉列表打开而不是向下?我试图将列表放在屏幕的下边缘。我在文档中找不到选项。
由于 ControlP5 使用 Java 泛型继承在这种情况下实际上非常棘手,应该覆盖 ScrollableListView
的 display()
方法:
ScrollableList extends Controller< ScrollableList >
- in java a class 可以 subclass 单个 class,而不是多个,因此定制的 subclass like
PopUpScrollableList
会继承 Controller< ScrollableList >
,而不是 Controller< PopUpScrollableList >
- 在
ScrollableListView
的 display()
中访问了一堆属于 ScrollableList
的属性,它们是 private
或 protected
TLDR;
@laancelot 建议克隆库,直接修改 ScrollableListView
的 display()
方法并重新编译库,这听起来比 subclassing 到自定义滚动列表更容易。
也就是说,设置一个 IDE 并编译一个 java 库对于初学者来说可能不是最友好的事情,因此我可以推荐一个 hacky 解决方法:简单地移动列表的 y 位置所以它似乎在上面而不是下面:
import controlP5.*;
import java.util.*;
ControlP5 cp5;
ScrollableList list;
int listX = 100;
int listY = 100;
int listWidth = 200;
int listHeight = 100;
int barHeight = 20;
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 */
list = cp5.addScrollableList("dropdown")
.setPosition(listX, listY)
.setSize(listWidth, listHeight)
.setBarHeight(barHeight)
.setItemHeight(20)
.addItems(l)
// .setType(ScrollableList.LIST) // currently supported DROPDOWN and LIST
;
}
void draw() {
// hack: shift the list up when it's open
if(list.isOpen()){
list.setPosition(listX, listY - listHeight + barHeight);
}else{
list.setPosition(listX, listY);
}
background(240);
}
它在视觉上并不完美,可能是一种潜在的不良用户体验,但它是使用 ControlP5 进行此自定义行为的最简单选项。
或者,值得考虑自定义一个不同的 UI 库或编写一个自定义列表。
为了论证,这里是 Guido library 列表示例的修改版本:
/**
* A list
*
*
* Make sure to try your scroll wheel!
*/
import de.bezier.guido.*;
Listbox listbox;
SimpleButton button;
Object lastItemClicked;
void setup ()
{
size(400, 400);
// make the manager
Interactive.make( this );
// create a list box
listbox = new Listbox( 20, 30, width-40, height-80 );
for ( int i = 0, r = int(10+random(100)); i < r; i++ )
{
listbox.addItem( "Item " + i );
}
listbox.visible = false;
// create button
button = new SimpleButton("pop-up list", 20, 350, width-40, 24 );
}
void draw ()
{
background( 20 );
listbox.visible = button.on;
if ( lastItemClicked != null )
{
fill( 255 );
text( "Clicked " + lastItemClicked.toString(), 30, 20 );
}
}
public void itemClicked ( int i, Object item )
{
lastItemClicked = item;
}
public class Listbox
{
float x, y, width, height;
ArrayList items;
int itemHeight = 20;
int listStartAt = 0;
int hoverItem = -1;
float valueY = 0;
boolean hasSlider = false;
boolean visible = true;
Listbox ( float xx, float yy, float ww, float hh )
{
x = xx; y = yy;
valueY = y;
width = ww; height = hh;
// register it
Interactive.add( this );
}
public void addItem ( String item )
{
if ( items == null ) items = new ArrayList();
items.add( item );
hasSlider = items.size() * itemHeight > height;
}
public void mouseMoved ( float mx, float my )
{
if(!visible){
return;
}
if ( hasSlider && mx > width-20 ) return;
hoverItem = listStartAt + int((my-y) / itemHeight);
}
public void mouseExited ( float mx, float my )
{
if(!visible){
return;
}
hoverItem = -1;
}
// called from manager
void mouseDragged ( float mx, float my )
{
if(!visible){
return;
}
if ( !hasSlider ) return;
if ( mx < x+width-20 ) return;
valueY = my-10;
valueY = constrain( valueY, y, y+height-20 );
update();
}
// called from manager
void mouseScrolled ( float step )
{
if(!visible){
return;
}
valueY += step;
valueY = constrain( valueY, y, y+height-20 );
update();
}
void update ()
{
if(!visible){
return;
}
float totalHeight = items.size() * itemHeight;
float itemsInView = height / itemHeight;
float listOffset = map( valueY, y, y+height-20, 0, totalHeight-height );
listStartAt = int( listOffset / itemHeight );
}
public void mousePressed ( float mx, float my )
{
if(!visible){
return;
}
if ( hasSlider && mx > width-20 ) return;
int item = listStartAt + int( (my-y) / itemHeight);
itemClicked( item, items.get(item) );
}
void draw ()
{
if(!visible){
return;
}
noStroke();
fill( 100 );
rect( x,y,this.width,this.height );
if ( items != null )
{
for ( int i = 0; i < int(height/itemHeight) && i < items.size(); i++ )
{
stroke( 80 );
fill( (i+listStartAt) == hoverItem ? 200 : 120 );
rect( x, y + (i*itemHeight), this.width, itemHeight );
noStroke();
fill( 0 );
text( items.get(i+listStartAt).toString(), x+5, y+(i+1)*itemHeight-5 );
}
}
if ( hasSlider )
{
stroke( 80 );
fill( 100 );
rect( x+width-20, y, 20, height );
fill( 120 );
rect( x+width-20, valueY, 20, 20 );
}
}
}
public class SimpleButton
{
float x, y, width, height;
boolean on;
String label = "";
SimpleButton ( float xx, float yy, float w, float h )
{
x = xx; y = yy; width = w; height = h;
Interactive.add( this ); // register it with the manager
}
SimpleButton ( String label, float xx, float yy, float w, float h )
{
this(xx, yy, w, h);
this.label = label;
}
// called by manager
void mousePressed ()
{
on = !on;
}
void draw ()
{
if ( on ) fill( 200 );
else fill( 100 );
rect(x, y, width, height);
if ( on ) fill( 100 );
else fill( 200 );
text(label, x + 10, y + this.height * 0.65);
}
}
草图中的代码更加冗长,但可以移至单独的 GUI 选项卡。
希望因为它更简单,所以可以比 ControlP5 更容易地操作自定义行为。
使用文档中的这个 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);
}
如何让下拉列表打开而不是向下?我试图将列表放在屏幕的下边缘。我在文档中找不到选项。
由于 ControlP5 使用 Java 泛型继承在这种情况下实际上非常棘手,应该覆盖 ScrollableListView
的 display()
方法:
ScrollableList extends Controller< ScrollableList >
- in java a class 可以 subclass 单个 class,而不是多个,因此定制的 subclass like
PopUpScrollableList
会继承Controller< ScrollableList >
,而不是Controller< PopUpScrollableList >
- 在
ScrollableListView
的display()
中访问了一堆属于ScrollableList
的属性,它们是private
或protected
TLDR;
@laancelot 建议克隆库,直接修改 ScrollableListView
的 display()
方法并重新编译库,这听起来比 subclassing 到自定义滚动列表更容易。
也就是说,设置一个 IDE 并编译一个 java 库对于初学者来说可能不是最友好的事情,因此我可以推荐一个 hacky 解决方法:简单地移动列表的 y 位置所以它似乎在上面而不是下面:
import controlP5.*;
import java.util.*;
ControlP5 cp5;
ScrollableList list;
int listX = 100;
int listY = 100;
int listWidth = 200;
int listHeight = 100;
int barHeight = 20;
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 */
list = cp5.addScrollableList("dropdown")
.setPosition(listX, listY)
.setSize(listWidth, listHeight)
.setBarHeight(barHeight)
.setItemHeight(20)
.addItems(l)
// .setType(ScrollableList.LIST) // currently supported DROPDOWN and LIST
;
}
void draw() {
// hack: shift the list up when it's open
if(list.isOpen()){
list.setPosition(listX, listY - listHeight + barHeight);
}else{
list.setPosition(listX, listY);
}
background(240);
}
它在视觉上并不完美,可能是一种潜在的不良用户体验,但它是使用 ControlP5 进行此自定义行为的最简单选项。
或者,值得考虑自定义一个不同的 UI 库或编写一个自定义列表。
为了论证,这里是 Guido library 列表示例的修改版本:
/**
* A list
*
*
* Make sure to try your scroll wheel!
*/
import de.bezier.guido.*;
Listbox listbox;
SimpleButton button;
Object lastItemClicked;
void setup ()
{
size(400, 400);
// make the manager
Interactive.make( this );
// create a list box
listbox = new Listbox( 20, 30, width-40, height-80 );
for ( int i = 0, r = int(10+random(100)); i < r; i++ )
{
listbox.addItem( "Item " + i );
}
listbox.visible = false;
// create button
button = new SimpleButton("pop-up list", 20, 350, width-40, 24 );
}
void draw ()
{
background( 20 );
listbox.visible = button.on;
if ( lastItemClicked != null )
{
fill( 255 );
text( "Clicked " + lastItemClicked.toString(), 30, 20 );
}
}
public void itemClicked ( int i, Object item )
{
lastItemClicked = item;
}
public class Listbox
{
float x, y, width, height;
ArrayList items;
int itemHeight = 20;
int listStartAt = 0;
int hoverItem = -1;
float valueY = 0;
boolean hasSlider = false;
boolean visible = true;
Listbox ( float xx, float yy, float ww, float hh )
{
x = xx; y = yy;
valueY = y;
width = ww; height = hh;
// register it
Interactive.add( this );
}
public void addItem ( String item )
{
if ( items == null ) items = new ArrayList();
items.add( item );
hasSlider = items.size() * itemHeight > height;
}
public void mouseMoved ( float mx, float my )
{
if(!visible){
return;
}
if ( hasSlider && mx > width-20 ) return;
hoverItem = listStartAt + int((my-y) / itemHeight);
}
public void mouseExited ( float mx, float my )
{
if(!visible){
return;
}
hoverItem = -1;
}
// called from manager
void mouseDragged ( float mx, float my )
{
if(!visible){
return;
}
if ( !hasSlider ) return;
if ( mx < x+width-20 ) return;
valueY = my-10;
valueY = constrain( valueY, y, y+height-20 );
update();
}
// called from manager
void mouseScrolled ( float step )
{
if(!visible){
return;
}
valueY += step;
valueY = constrain( valueY, y, y+height-20 );
update();
}
void update ()
{
if(!visible){
return;
}
float totalHeight = items.size() * itemHeight;
float itemsInView = height / itemHeight;
float listOffset = map( valueY, y, y+height-20, 0, totalHeight-height );
listStartAt = int( listOffset / itemHeight );
}
public void mousePressed ( float mx, float my )
{
if(!visible){
return;
}
if ( hasSlider && mx > width-20 ) return;
int item = listStartAt + int( (my-y) / itemHeight);
itemClicked( item, items.get(item) );
}
void draw ()
{
if(!visible){
return;
}
noStroke();
fill( 100 );
rect( x,y,this.width,this.height );
if ( items != null )
{
for ( int i = 0; i < int(height/itemHeight) && i < items.size(); i++ )
{
stroke( 80 );
fill( (i+listStartAt) == hoverItem ? 200 : 120 );
rect( x, y + (i*itemHeight), this.width, itemHeight );
noStroke();
fill( 0 );
text( items.get(i+listStartAt).toString(), x+5, y+(i+1)*itemHeight-5 );
}
}
if ( hasSlider )
{
stroke( 80 );
fill( 100 );
rect( x+width-20, y, 20, height );
fill( 120 );
rect( x+width-20, valueY, 20, 20 );
}
}
}
public class SimpleButton
{
float x, y, width, height;
boolean on;
String label = "";
SimpleButton ( float xx, float yy, float w, float h )
{
x = xx; y = yy; width = w; height = h;
Interactive.add( this ); // register it with the manager
}
SimpleButton ( String label, float xx, float yy, float w, float h )
{
this(xx, yy, w, h);
this.label = label;
}
// called by manager
void mousePressed ()
{
on = !on;
}
void draw ()
{
if ( on ) fill( 200 );
else fill( 100 );
rect(x, y, width, height);
if ( on ) fill( 100 );
else fill( 200 );
text(label, x + 10, y + this.height * 0.65);
}
}
草图中的代码更加冗长,但可以移至单独的 GUI 选项卡。 希望因为它更简单,所以可以比 ControlP5 更容易地操作自定义行为。