This will clean the "view" part. But, as already said, Swing is an MVC framework. So you are still missing the "m" and "c" parts...
Model
The model must be where your "state" is stored. Ideally this is the only class where you set and get the text. Let's create an Equation class for it and add all the required method on it (I amappend, clear, dropLast, ..)
So the Gui change this model when he receive an event from his ButtonsPanel. And you ends-up a line that is duplicate for each kind of event:
display.setText(equation.getText());
In Swing, the model is usually observable, so you can also add some listeners to react when he change. With this observable model your Gui will receive events form the buttons and update the model. When updated, the model will notify the text (via the event listener) and the text will change.
+---------------+ +-----+ +-----------+ +-----------+
| ButtonsPanel | | Gui | | Equation | | TextPanel |
+---------------+ +-----+ +-----------+ +-----------+
| | | |
| onInput | | |
|------------>| | |
| | | |
| | append | |
| |------------->| |
| | | |
| | | onChange |
| | |--------------->|
| | | |
| | | | setText
| | | |--------
| | | | |
| | | |<-------
| | | |
You can notice that from one side, there is ane actor between the event and the model (the Gui between ButtonsPanel and Equation), while on the other side there is no actor between the model and one view (Equation to TextPanel). This incoherence lead us to a little architectural discussion.
It is up to you to decide who will listen and react to an event. But, in my case, I rely on the smart and dumb containers pattern. Where I have one smart component, the Gui and the two dumbs, ButtonsPanel and TextPanel. So that the Gui is a kind of mediator between the model and the views.
+-----------+ +-----+ +---------------+ +-----------+
| Equation | | Gui | | ButtonsPanel | | TextPanel |
+-----------+ +-----+ +---------------+ +-----------+
| | | |
| | onInput | |
| |<-----------------| |
| | | |
| append | | |
|<------------| | |
| | | |
| onChange | | |
|------------>| | |
| | | |
| | setText | |
| |--------------------------------->|
| | | |
In fact the Gui has too much responsibility, because it play both the role of a view (a smart container) and the role of a controller.
Controller
Let's segregate the roles of the Gui is role will be to deal with the presentation and redirect events to a controller. On the other side the controller will update the model according to the received actions.
In some systems the controller implements all the required listeners. This is sometime not the best one for code reuse because the controller depends on Swing. Also it require to expose the listeners of all of your components or create adapters between the listeners for the dumb components and those of the smart components. But it is useful to reduce the number of delegation or decoration methods. The biggest advantage is that it force you to create one model that represent the state of your application (or part of it) so that any change can be observed by the view.
Finally
A this time you should have a clean MVC :
+-------+ +-------+ +---------------+ +-----------+ +-------------+
| Model | | View | | ButtonsPanel | | TextPanel | | Controller |
+-------+ +-------+ +---------------+ +-----------+ +-------------+
| | | | |
| | onButtonPressed | | |
| |<-------------------------| | |
| | | | |
| | // call method for the pressed button | |
| |-------------------------------------------------------->|
| | | | |
| | | // call mutation method |
|<------------------------------------------------------------------------|
| | | | |
| onChange | | | |
|-------------->| | | |
| | | | |
| | setText | | |
| |----------------------------------------->| |
| | | | |
Model
class Model {
private final EventListenerList listeners = new EventListenerList();
private final StringBuilder content;
public Model() {
this.content = new StringBuilder();
}
public void setResult(String result) {
fireOnResult(result);
}
public void append(String part) {
content.append(part);
fireOnChange();
}
public void clear() {
content.delete(0, content.length());
fireOnChange();
}
public void dropLast() {
content.deleteCharAt(content.length());
fireOnChange();
}
public String getText() {
return content.toString();
}
public void addListener(ModelListener listener) {
listeners.add(ModelListener.class, listener);
}
public void removeListener(ModelListener listener) {
listeners.remove(ModelListener.class, listener);
}
private void fireOnChange() {
ModelListener[] lstnrs = listeners.getListeners(ModelListener.class);
for (int i=lstnrs.length-1; i > -1; i--) {
lstnrs[i].onEquationChange(content.toString());
}
}
private void fireOnResult(String result) {
ModelListener[] lstnrs = listeners.getListeners(ModelListener.class);
for (int i=lstnrs.length-1; i > -1; i--) {
lstnrs[i].onResult(result);
}
}
}
View
class View extends JFrame {
private final Controller controller;
private final ButtonsPanel buttons;
private final TextPanel display;
public View(final Controller controller) {
this.controller = controller;
setSize(400, 400);
setDefaultCloseOperation(EXIT_ON_CLOSE);
add(display = new TextPanel(), BorderLayout.NORTH);
add(buttons = new ButtonsPanel(), BorderLayout.SOUTH);
buttons.addListener(new ButtonsListener());
controller.addModelListener(new TextUpdater());
}
private final class TextUpdater implements ModelListener {
private void updateText(String newText) {
SwingUtilities.invokeLater(() -> display.setText(newText));
}
@Override
public void onEquationChange(String equation) {
updateText(equation);
}
@Override
public void onResult(String result) {
updateText(result);
}
}
private final class ButtonsListener implements ButtonsPanel.ButtonsPanelListener {
@Override
public void onButtonPressed(String text) {
new SwingWorker<Void, Void>(){
@Override
protected Void doInBackground() throws Exception {
switch (text) {
case ButtonsPanel.CLEAR:
controller.clear();
break;
case ButtonsPanel.ERASE:
controller.eraseOne();
break;
case ButtonsPanel.EQUAL:
controller.compute();
break;
default:
controller.onInput(text);
}
return null;
}
}.execute();
}
}
}
Controller
class Controller {
private final Engine engine;
private final Model model;
public Controller(Engine engine) {
this.engine = engine;
this.model = new Model();
}
public void compute() throws Exception {
String result = engine.compute(model.getText());
model.setResult(result);
}
public void onInput(String text) {
model.append(text);
}
public void clear() {
model.clear();
}
public void eraseOne() {
model.dropLast();
}
public void addModelListener(ModelListener listener) {
model.addListener(listener);
}
public void removeModelListener(ModelListener listener) {
model.removeListener(listener);
}
}