I'm working on a GUI library for Processing, and I have got a class Component
. I have a bunch of other classes that extend Component
: Label
, Button
(Which extends Label
), Panel
and Icon
. These classes continue just the "logic" part (position, text, function checking if they are clicked etc...).
The class Panel
has inside of it an ArrayList
of Component
, and you can add to it buttons, labels etc...
To display the components I decided to use a class Theme
, which has inside some overloadings of the function display(Component component)
(one for Label
, one for Button
, one for Panel
etc...).
Inside display(Panel panel)
, at some point, I display all the children of the panel, but, instead of treating them (the children) as Button
or Label
, it treats all of them as Component
. How can I solve this ISSUE?
I've already tried to use a method display()
inside the classes Component
, Button
etc... it works partly, but it gives some "coding problems": to override the function display you have to create a new class (Example: GreenBackgroundButton extends Button{}). Also, by putting all the function in an external class, you can control all the grapihcs of you Program with a single class, so you can have the same background for all the components with a single function displayBackground()
etc...
instanceof
can't be used, because, if I have to use it to cast the children of the panel, the user can't create custom components, because they would be displayed as Component
not as TextField
(example).
I've tried casting directly display((Button)panel.getChildren.get(0))
, but, as it Should, when I use a label, it gives an error (as expected).
I've tried casting like this too: panel.getChildren().get(0).getClass().cast(panel.getChildren().get(0))
, and it doesn't work, I don't know why. (I tried all its variants, like creating an external object etc...)
If you need more code, you can ask...
public void display(Panel panel) {
println("panel");
if (panel.isVisible()) {
pushMatrix();
translate(panel.getX(), panel.getY());
displayBackground(panel, #AAAAAA);
for (int i = 0; i < panel.getChildren().size(); i++) {
this.display(panel.getChildren().get(i).getClass().cast(panel.getChildren().get(i))); //THIS IS THE LINE WITH THE ISSUE
//println(panel.getChildren().get(i).getClass().cast(panel.getChildren().get(i)));
}
popMatrix();
}
}
Theme basicTheme;
Button close;
Panel panel;
void settings() {
size(400, 600, P2D);
}
void setup() {
basicTheme = new Theme();
close = new Button();
close.setBounds(10, 10, 100, 25);
close.setText("close");
panel = new Panel();
panel.setBounds(50, 200, 200, 200);
panel.add(close);
}
void draw() {
background(255);
basicTheme.display(panel);
}
I expect to see on the canvas a working button inside the panel, instead of seeing a component. I don't have any particular error I can think of right now.
Using instanceof
is almost always a hack, and is probably a symptom of a bad design.
What you've described sounds exactly like an existing Java library called the Abstract Window Toolkit (AWT), so you might consider "borrowing" some of their design patterns.
Component
is an abstract class that defines an abstract paint()
function. The paint()
function takes a Graphics
argument. More on that in a second.Button
and Label
extend Component
and overide the paint()
function.Graphics
is a class that contains functions like drawRect()
and setBackground()
. It encapsulates shared drawing code, similar to what I think your Theme
class would do.If I were you, I would consider taking a similar approach for your library.
I'll try to address some of your other comments:
Example: GreenBackgroundButton extends Button{}
Favor composition over inheritance for cases like this. You should not need to extend Button
to set a background. Instead, have a setBackground()
function in the Button()
class (or in the Component
class).
Also, by putting all the function in an external class, you can control all the rapihcs of you Program with a single class, so you can have the same background for all the components with a single function displayBackground() etc.
It sounds like this belongs in your Component
class.
Also, taking a step back, I would encourage you to avoid the temptation to over-engineer a solution. You might think you need a complicated design with several levels of inheritance and many different classes, but chances are a simpler design will probably get you almost everything you need. Specifically, I don't see a huge benefit to extracting any code into the Theme
class. I would probably put everything directly in my component subclasses like Button
and Label
.