Using MVC with JUCE

#1

Could somebody outline how to set up a JUCE application that follows MVC?

I have watched a video from Vlad Voina in which he strongly advises to adopt an MVC approach for building the UI. The link is here : https://www.youtube.com/watch?v=YW1HS-hQY78&t=1656s

Unfortunately, the presentation offers no insight into how to wire controllers and views together or how to implement the boilerplate code that would be necessary to separate the code into views and controllers. The small examples of code he shows have been reduced for the sake of brevity and don’t provide any insight into how they fit together.

I would really like to follow an MVC architecture and I have good experience with it when building .NET applications - but then most of the plumbing is already done for you.

It seems to me that the Main Window that inherits from DocumentWindow appears somewhat like a typical controller - it resolves the view through setContentOwned and houses the logic for closeButtonPressed. The MainComponent houses much of what I would consider belongs to a view - layout logic, paint and resize. However, it also includes aspects that I consider should be part of the controller such as buttonClicked handlers.

Does anyone follow a conventional MVC design with JUCE? If so, how are you doing it? How do you route between different views and controllers?

0 Likes

#2

Check out @dave96’s “ValueTree” talk on youtube.

2 Likes

#3

The MVC-ish stuff I’ve done with JUCE looks like this:

The view is normally a Component which contains further Components. It will have some custom resized functionality to lay out those components. For completely custom controls (new types of slider/selector/button) it might need a ListenerList (or some std::function) members, so that the component can send notifications that the user has interacted in some way. The view component should have no knowledge of the model whatsoever.

The controller will take a reference to the view as an argument to the constructor. It will probably also take a reference to the model. In the constructor body, it will register itself as a listener of the view. It will supply appropriate listener/function callbacks so that when the user interacts with the view, those changes are propagated to the model. The controller will also register as a listener of the model, so that when it changes in any way, those changes are used to update the view. Ideally, the only modifications to the view happen within a model callback, and the only modifications to the model happen within a view callback.

1 Like

#4

This is my opinion, people who wants to do all by the book might disagree:

MVC is a design pattern, that is very useful, if you design an API, since you can change the behaviour in the controller (in JUCE world the mouseDown/Up/Drag events), the displaying in the view (paint() and resized()) and have the data in the model, for access of the business code, where the work is done (be it DSP rendering or whatever your app is implementing).

JUCE was not designed, with this pattern 100% in mind. You find traces of it, but at the same time, another paradigm became strong: aggregation over inheritance. That’s why Controller and View functionality was delegated to the LookAndFeel classes. It has the benefit, that the same view implementation can be applied to any Slider, or any Button, regardless of what it inherits.

That’s why when using JUCE, I don’t follow the the MVC to the book, since it adds overhead and unclarity, e.g. a button, is it owned by Controller or by the View? An argument can be found for both. And you don’t want to be wobbly with ownership, that is the most crucial point for stability.

For trivial tasks, I have the containing Component as my Model, and each Button, Slider etc. is it’s own MVC entity, updating the model from it’s callbacks, e.g. by being a Listener (NOT mouseListener!)

And in case of an AudioPlugin, the AudioProcessorValueTreeState is my Model.

For more Complex things, I try to write Models, that are not Components at the same time, so I can have them in memory, regardless, if they are currently represented on the screen or not. Also, I might implement different views for the same model, e.g. as Timeline or as Properties widget, allowing numeric input. And maybe display both at the same time.

5 Likes

#5

Thanks, I understand how interaction between models, views and components should flow - I use MVC on a daily basis. What i’m struggling with is the boilerplate/infrastructure that would be needed to modify a typical JUCE application to use MVC. You mention that the controller has a reference to the view and model but which class is responsible for instantiating the controller? It needs to be something that knows about the view and the model because it will be providing a reference to both in order to construct a controller. I have done this in .NET using webforms and it was necessary to hook into the lifecycle of the view and inject the correct controller at the right time when the view was being instantiated. I’m guessing that most frameworks rely on some kind of dependency injection under the hood to get this done. Is this required in JUCE too?

0 Likes

#6

That is exactly my point, to deviate from MVC. Neither of the classes are agnostic to the other, nor generic enough to be recycled. So IMHO there is no point, to always create two separate classes for that. There might be in some cases.

Thanks @Chi79 for bringing that up

0 Likes

#7

Interesting. Does ‘aggregation over inheritance’ mean something similar to ‘composition over inheritance’? If so, that is something I can strongly relate to and prefer it in almost all circumstances.

Is ‘LookAndFeel’ really both controller and view rolled into one? I would have thought it was ‘front-end’ by nature and concerned with presentation logic thus not really something the controller or model should care about. I would have treated it in a similar way as styling in a web app such as CSS. When you ask who owns a button, I would consider things like how the button looks and is displayed as belonging to the view and front-end but, once the button receives any input and is clicked, the click behavior is the controllers responsibility.

0 Likes

#8

You are right, I might have picked up the wrong term. That’s actually what I meant.
I heard them both, but it came up a while after I left uni, so I might have got it from dubious sources :wink:

Browsing the methods of the LookAndFeel implementations, I might have seen it as wishful thinking. I always wondered, how the drawing would be decoupled from mouse events, e.g. if I wanted to implement a curved slider?

0 Likes

#9

Thanks, yes this is a great video - value trees appear to be at the heart of the model in JUCE. Unfortunately, my difficulty at the moment is trying to find out how to wire-up views with their controllers and manage their lifecycles correctly. Can anybody provide some intel? :upside_down_face:

I really wish Vlad Voina had provided a simple example of how he achieves this. Is he on the forum here in any capacity I wonder?

0 Likes

#10

I have to admit, I haven’t inspected too deeply the LookAndFeel but I have overidden some LookAndFeel methods to make a custom slider. Are mouse events intertwined with the draw methods of look and feel somehow? I didn’t notice that. But, what happens on a mouse event with respect to behavior that can affect the model should be easily separable from any visual changes that the click might trigger. The controller need only subscribe to the click event for this right?

0 Likes

#11

If you use the Audio Plugin design as your guide:
the gui widgets are part of the view,
the audio parameters are the model,
and the AudioProcessorValueTreeState::____Attachment classes are the controller.

the parameters get changed, and the APVTS tell the Slider/Button/ComboBoxAttachment classes to update, which in turn update the “view”.

likewise, the sliders and buttons get moved or clicked, those Attachment classes are listening to those movements, and they update the parameters.

0 Likes

#12

Is this a tutorial/demo project you’re referring to?

0 Likes

#13

no, this is how every plugin gets written. But have a look at the tutorials under ‘learn’ that show how to write a plugin.

0 Likes

#14

Are you suggesting that every plugin written in JUCE is structured as MVC?

0 Likes

#15

the audio parameters form the Model
the GUI widgets are the View
the APVTS or ParameterListener/Slider::Listener classes are the thing that connect the view to the model so they stay in sync.

I think you’re getting too hung up on terminology instead of just working on your project and making it do cool stuff.

0 Likes