package org.multibit.hd.ui.views.screens;
import com.google.common.base.Preconditions;
import com.google.common.eventbus.Subscribe;
import org.multibit.hd.core.events.CoreEvents;
import org.multibit.hd.ui.events.view.ScreenComponentModelChangedEvent;
import org.multibit.hd.ui.events.view.ViewEvents;
import org.multibit.hd.ui.languages.MessageKey;
import org.multibit.hd.ui.views.components.Panels;
import org.multibit.hd.ui.views.components.panels.PanelDecorator;
import javax.swing.*;
/**
* <p>Abstract base class providing the following to screens:</p>
* <ul>
* <li>Standard methods common to screens</li>
* </ul>
* <p>A screen contains the title and components. It relies on
* its implementers to provide the panel containing the specific
* components for the user interaction.</p>
*
* @param <M> The screen model
*
* @since 0.0.1
*
*/
public abstract class AbstractScreenView<M extends ScreenModel> {
private final M screenModel;
private final Screen screen;
/**
* The current screen view panel with the specific components for the view
*/
private JPanel currentScreenViewPanel;
/**
* True if the components making up this screen have been populated
*/
private boolean initialised;
/**
* @param screenModel The screen model managing the states
* @param screen The screen to filter events from components
* @param title The key to the main title of the wizard panel
*/
public AbstractScreenView(M screenModel, Screen screen, MessageKey title) {
Preconditions.checkNotNull(screenModel, "'screenModel' must be present");
Preconditions.checkNotNull(screen, "'screen' must be present");
Preconditions.checkNotNull(title, "'title' must be present");
this.screenModel = screenModel;
this.screen = screen;
// All detail views can receive events
ViewEvents.subscribe(this);
CoreEvents.subscribe(this);
// All screens are decorated with the same theme and layout at creation
// so just need a vanilla panel to begin with
JPanel screenPanel = Panels.newPanel();
// All detail panels require a backing model
newScreenModel();
// Apply the screen theme to the panel
PanelDecorator.applyScreenTheme(screenPanel, title);
}
/**
* This screen is closing (probably due to a shutdown)
*/
public void unsubscribe() {
ViewEvents.unsubscribe(this);
CoreEvents.unsubscribe(this);
}
/**
* @return The screen model providing aggregated state information
*/
public M getScreenModel() {
return screenModel;
}
/**
* @return The screen
*/
public Screen getScreen() {
return screen;
}
/**
* <p>Called when the screen is first created to initialise the model and subsequently on a locale change event.</p>
*
* <p>This is called before {@link AbstractScreenView#initialiseScreenViewPanel()}</p>
*
* <p>Implementers must create a new panel model and bind it to the overall screen</p>
*/
public abstract void newScreenModel();
/**
* @return The current screen view panel with lazy initialisation
*/
public JPanel getScreenViewPanel() {
if (!isInitialised()) {
currentScreenViewPanel = initialiseScreenViewPanel();
setInitialised(true);
}
return currentScreenViewPanel;
}
/**
* <p>Called when the screen is first created to initialise the panel and subsequently on a locale change event.</p>
*
* <p>This is called after {@link AbstractScreenView#newScreenModel()}</p>
*
* <p>Implementers must create a new panel</p>
*
* @return A new panel containing the data components specific to this screen (e.g. contacts or payments)
*/
protected abstract JPanel initialiseScreenViewPanel();
/**
* <p>Update the view with any required view events to create a clean initial state (all initialisation will have completed)</p>
*
* <p>Default implementation is to do nothing</p>
*/
public void fireInitialStateViewEvents() {
// Do nothing
}
/**
* <p>Called before this screen is about to be shown</p>
*
* <p>Typically this is where a view would reference the model to obtain latest values for display</p>
*
* @return True if the panel can be shown, false if the show operation should be aborted
*/
public boolean beforeShow() {
return true;
}
/**
* <p>Called after this screen has been shown</p>
*
* <p>Typically this is where a view would attempt to set the focus for its primary component using
* the Swing thread as follows:</p>
*
* <pre>
* SwingUtilities.invokeLater(new Runnable() {
*
* {@literal @}Override public void run() {
* myComponent.requestFocusInWindow();
* }
*
* });
*
* </pre>
*/
public void afterShow() {
// Do nothing
}
/**
* @return True if the implementer has configured all the components for display (screen can be shown)
*/
public boolean isInitialised() {
return initialised;
}
public void setInitialised(boolean initialised) {
this.initialised = initialised;
}
/**
* <p>React to a "screen component model changed" event</p>
*
* @param event The event
*/
@Subscribe
public void onScreenComponentModelChangedEvent(ScreenComponentModelChangedEvent event) {
}
}