Recommended way to extend JUCE Components?


#1

Hi there,

I'm a new user of JUCE (client work for a commercial project -- audio plugins).

I'm wondering what is the recommended way to extend the functionality of JUCE GUI components. I would like to change ComboBox to allow for multiple selections. Conceptually it's easy enough to do. In practice however it seems like ComboBox *was not* meant to be subclasses as much of the required functionality and base classes are private.

I'd copy and modify the class outright (though I hate to do so) but I'd like to be able to continue to use the Introjucer for layout/changing settings.

Most of my C++ experience has been writing algorithms and not dealing with the language's OO features, so it's possible I'm missing some obvious stuff.

Anyone have experience extending the functionality of components? What's the cleanest way to do it?

Thanks!


#2

Normally you should be able to do anything you need with a LookAndFeel class or a subclass of the component.. If you're doing something wildly different then you may need to just write your own class (possibly stealing bits from the juce components). But if you come across any small things that seem to be missing from the base class, then do let me know as I will add features on request if they're general enough.


#3

(clicked the wrong reply button)


#4

Excuse the messy thread I was asking questions that I answered for myself.

My conclusions:

While it's not possible to subclass ComboBox (and I would assume the other component classes) it is possible to create new ones. These classes are tightly coupled to LookAndFeel; however, LookAndFeel was designed to be subclassed, which makes that less of an issue.

If this isn't the recommended way to extend the GUI please let me know. If not I'll assume that I'm taking the path of least resistance.

Thanks


#5

(sorry for the noise)


#6

Unfortunately for deep tinkerers, one of the most important principles in maintaining public classes is to keep as much internal implementation private as possible. Unless you keep the internals decoupled from users' code, it would be impossible to maintained or improve the base-class without breaking everybody's projects.

In your case, where you're trying to make the ComboBox handle multi-selection, the reason you're getting thwarted is because to add that behaviour, you'd need to rewrite a massive amount of the class's internal guts. When you have to override almost every one of a class's methods to do what you need, it should be a hint that maybe inheritance is the wrong way to go, and you need your own class!

TBH I think wanting a multi-select combo-box is probably an unwise GUI design anyway. Whenever I've seen UI that does such a thing, people use either a button that pops-up a menu where you can tick multiple items, or they use a listbox where you can tick the items. I can't really imagine what you'd expect a combo-box to show in its textbox when there are multiple items.. The component just foesn't fit that paradigm IMHO.


#7

Let's not have a discussion about good GUI design for the moment.

ComboBox isn't subclass-able (for my purposes) because of what's been made private, not because it's a gross departure from the original functionality.

The problem with re-writing it is it's tighly coupled to LookAndFeel. While it's true that I can subclass LookAndFeel, it gets passed to the component via Component, resulting in C++'s classic slicing problem -- any methods I add to LookAndFeel (via my subclass) get sliced off which means I can't add the requisite methods to LookAndFeel to get it to draw my new component.

As it stands I can't see any way to write a custom Component that leverages LookAndFeel. Certainly that can't be the case. Can you comment directly on that?


#8

Can you fake it up convincingly by popping up a ListBox?


#9

Popup menus don't mind having more than one item selected (i.e. showing a checkmark) so I would probably just attach one of those to something, which is what ComboBox does anyway.

I would like to know if it's possible to write custom subclasses of Component (in light of the issue I mentioned above) so I know whether or not I can keep it on the table as an option.


#10

Sorry, I really don't understand your question about subclassing components with lookandfeel, and what you're saying about C++ slicing makes me think that maybe you don't properly understand what the slicing problem really is, because that's certainly not involved here.. 

But any component can have any lookandfeel, so you can do pretty much whatever you want with them..


#11

Ah, I thought the slicing problem occured when passing by reference and value. Again, pretty new to C++-only projects.

Here's what's happening:

1. I copied ComboBox and, in essence, changed an int to a SortedSet<int>.

2. Can't pass MyComboBox to LookAndFeel as a ComboBox since I can't cast one to other (they're similar but unrelated).

3. Subclassed LookAndFeel to add methods that will accept MyComboBox as a parameter.

4. Where ComboBox would call getLookAndFeel() I do so and cast LookAndFeel -> MyLookAndFeel.

5. dynamic_cast throws a SIGABRT. As I understand it dynamic_cast will crap its pants when "mis-casting" a reference (as opposed to returning null on a bad pointer cast). I assume that's what I'm seeing here. Maybe not.

6. A static_cast will throw an exception (bad instruction).

The latter is what I misinterpreted as a slicing issue. Any reason you can think of why that casting wouldn't work?

To Jules or anyone who can throw in some insight, thank you.

 

 


#12

Ah right - I see what you mean now.

Ok, well if you *really* need to call the LookAndFeel methods that require a combobox, then you would have to inherit from ComboBox. Of course that doesn't mean that you have to expose any of the underlying functionality, you could override every method in it with your own replacements if necessary.

(It's unfortunate that if you're new to C++ and juce, you've chosen an unusually awkward edge-case thing to try to do!)


#13

Your cast fails because you probably haven’t set the default LookAndFeel instance to an instance of your class.
If the instance passed in is not an instance of MyLookAndFeel, but of, say, LookAndFeel, it cannot be cast.
The difference in dynamic_cast<> and static_cast<> is not in the least related to slicing.

The issue in question is a failing cast to an inherited class from a base class reference, because the instance passed in is not an instance
of the inherited class. Thus, the cast is behaving exactly as it should.

In general, I would second jules on his opinion that a ComboBox semantically makes no sense with multiple selection.
A ComboBox is intended to select none or exactly one option from a list of possibilities; thus, a callout box with a list box in it that would
allow for multiple selection would be the semantically correct choice, since it conforms with the user’s expectations.
A ComboBox with the ability to select multiple items is not conforming to expectations, on the other hand. And in the end of the day,
GUI design is about what the user expects and understands.

(forget the crap I wrote earlier)


#14

If you are using a C++11 capable compiler, the new language specification actually makes it easier for you to override even private methods, but you need to be explicit about it.


#15

Thanks both. I'll look into it some more and see what I can sort out.