skeleton of setup wizard

This commit is contained in:
Zlatin Balevsky
2020-05-28 20:08:57 +01:00
parent bf28278f72
commit c041f6baaa
8 changed files with 310 additions and 21 deletions

View File

@ -141,4 +141,9 @@ mvcGroups {
view = 'com.muwire.gui.SignView'
controller = 'com.muwire.gui.SignController'
}
'wizard' {
model = 'com.muwire.gui.wizard.WizardModel'
view = 'com.muwire.gui.wizard.WizardView'
controller = 'com.muwire.gui.wizard.WizardController'
}
}

View File

@ -0,0 +1,47 @@
package com.muwire.gui.wizard
import griffon.core.artifact.GriffonController
import griffon.core.controller.ControllerAction
import griffon.inject.MVCMember
import griffon.metadata.ArtifactProviderFor
import javax.annotation.Nonnull
import javax.swing.JOptionPane
@ArtifactProviderFor(GriffonController)
class WizardController {
@MVCMember @Nonnull
WizardModel model
@MVCMember @Nonnull
WizardView view
@ControllerAction
void previous() {
model.currentStep--
recalcButtons()
view.updateLayout()
}
@ControllerAction
void next() {
def errors = model.steps[model.currentStep].validate()
if (errors) {
String errorMessage = String.join("\n", errors)
JOptionPane.showMessageDialog(model.parent, errorMessage, "Invalid Input", JOptionPane.ERROR_MESSAGE)
} else {
model.currentStep++
recalcButtons()
view.updateLayout()
}
}
@ControllerAction
void finish() {
}
private void recalcButtons() {
model.previousButtonEnabled = model.currentStep > 0
model.nextButtonEnabled = model.steps.size() > (model.currentStep + 1)
model.finishButtonEnabled = model.steps.size() == (model.currentStep + 1)
}
}

View File

@ -54,27 +54,41 @@ class Ready extends AbstractLifecycleHandler {
} else {
log.info("creating new properties")
props = new MuWireSettings()
props.incompleteLocation = new File(home, "incompletes")
props.embeddedRouter = Boolean.parseBoolean(System.getProperties().getProperty("embeddedRouter"))
props.updateType = System.getProperty("updateType","jar")
props.setNickname(selectNickname())
def portableDownloads = System.getProperty("portable.downloads")
if (portableDownloads != null) {
props.downloadLocation = new File(portableDownloads)
} else {
def chooser = new JFileChooser()
chooser.setFileHidingEnabled(false)
chooser.setDialogTitle("Select a directory where downloads will be saved")
chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY)
int rv = chooser.showOpenDialog(null)
if (rv != JFileChooser.APPROVE_OPTION) {
JOptionPane.showMessageDialog(null, "MuWire will now exit")
System.exit(0)
}
props.downloadLocation = chooser.getSelectedFile()
}
boolean embeddedRouterAvailable = Boolean.parseBoolean(System.getProperties().getProperty("embeddedRouter"))
def parent = application.windowManager.findWindow("event-list")
Properties i2pProps = new Properties()
def params = [:]
params['parent'] = parent
params['embeddedRouterAvailable'] = embeddedRouterAvailable
params['muSettings'] = props
params['i2pProps'] = i2pProps
application.mvcGroupManager.createMVCGroup("wizard", params)
// props.incompleteLocation = new File(home, "incompletes")
// props.embeddedRouter = Boolean.parseBoolean(System.getProperties().getProperty("embeddedRouter"))
// props.updateType = System.getProperty("updateType","jar")
// props.setNickname(selectNickname())
//
//
// def portableDownloads = System.getProperty("portable.downloads")
// if (portableDownloads != null) {
// props.downloadLocation = new File(portableDownloads)
// } else {
// def chooser = new JFileChooser()
// chooser.setFileHidingEnabled(false)
// chooser.setDialogTitle("Select a directory where downloads will be saved")
// chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY)
// int rv = chooser.showOpenDialog(null)
// if (rv != JFileChooser.APPROVE_OPTION) {
// JOptionPane.showMessageDialog(null, "MuWire will now exit")
// System.exit(0)
// }
// props.downloadLocation = chooser.getSelectedFile()
// }
propsFile.withPrintWriter("UTF-8", {
props.write(it)

View File

@ -0,0 +1,32 @@
package com.muwire.gui.wizard
import java.awt.Component
import com.muwire.core.MuWireSettings
import griffon.core.artifact.GriffonModel
import griffon.transform.Observable
import griffon.metadata.ArtifactProviderFor
@ArtifactProviderFor(GriffonModel)
class WizardModel {
Component parent
boolean embeddedRouterAvailable
MuWireSettings muSettings
Properties i2pProps
final List<WizardStep> steps = [new NicknameStep(),
new DirectoriesStep()]
int currentStep
@Observable boolean finishButtonEnabled
@Observable boolean previousButtonEnabled
@Observable boolean nextButtonEnabled
void mvcGroupInit(Map<String,String> args) {
currentStep = 0
previousButtonEnabled = false
nextButtonEnabled = steps.size() > (currentStep + 1)
finishButtonEnabled = steps.size() == currentStep + 1
}
}

View File

@ -0,0 +1,63 @@
package com.muwire.gui.wizard
import griffon.core.artifact.GriffonView
import griffon.inject.MVCMember
import griffon.metadata.ArtifactProviderFor
import javax.swing.JDialog
import javax.swing.SwingConstants
import java.awt.BorderLayout
import java.awt.event.WindowAdapter
import java.awt.event.WindowEvent
import javax.annotation.Nonnull
@ArtifactProviderFor(GriffonView)
class WizardView {
@MVCMember @Nonnull
FactoryBuilderSupport builder
@MVCMember @Nonnull
WizardModel model
def dialog
def p
void initUI() {
dialog = new JDialog(model.parent, "Setup Wizard", true)
p = builder.panel {
borderLayout()
panel (id : "cards-panel", constraints : BorderLayout.CENTER) {
cardLayout()
model.steps.each {
it.buildUI(builder)
}
}
panel (constraints : BorderLayout.SOUTH) {
button(text : "Previous", enabled : bind {model.previousButtonEnabled}, previousAction)
button(text : "Next", enabled : bind {model.nextButtonEnabled}, nextAction)
button(text : "Finish", enabled : bind {model.finishButtonEnabled}, finishAction)
}
}
}
void updateLayout() {
String constraints = model.steps[model.currentStep].getConstraint()
def cardsPanel = builder.getVariable("cards-panel")
cardsPanel.getLayout().show(cardsPanel, constraints)
}
void mvcGroupInit(Map<String,String> args) {
dialog.getContentPane().add(p)
dialog.pack()
dialog.setLocationRelativeTo(model.parent)
dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE)
dialog.addWindowListener( new WindowAdapter() {
public void windowClosed(WindowEvent e) {
mvcGroup.destroy()
}
})
dialog.show()
}
}

View File

@ -0,0 +1,72 @@
package com.muwire.gui.wizard
import javax.swing.JFileChooser
import com.muwire.core.MuWireSettings
class DirectoriesStep extends WizardStep {
def downloadLocationField
def incompleteLocationField
def downloadLocationButton
def incompleteLocationButton
public DirectoriesStep(String constraint) {
super("directories")
}
@Override
protected void buildUI(FactoryBuilderSupport builder) {
File defaultDownloadLocation = new File(System.getProperty("user.home"), "MuWire Downloads")
File defaultIncompleteLocation = new File(System.getProperty("user.home"), "MuWire Incompletes")
builder.panel(constraints : getConstraint()) {
gridBagLayout()
label(text : "Select directories for saving downloaded and incomplete files. They will be created if they do not already exist",
constraints : gbc(gridx: 0, gridy: 0, gridwidth : 2))
label(text : "Directory for saving downloaded files", constraints : gbc(gridx:0, gridy: 1))
downloadLocationField = textField(text : defaultDownloadLocation.getAbsolutePath(), constraints : gbc(gridx : 0, gridy : 2))
downloadLocationButton = button(text : "Choose", constraints : gbc(gridx: 1, gridy: 2), actionPerformed : showDownloadChooser)
label(text : "Directory for storing incomplete files", constraints : gbc(gridx:0, gridy: 3))
incompleteLocationField = textField(text : defaultIncompleteLocation.getAbsolutePath(), constraints : gbc(gridx:0, gridy:4))
incompleteLocationButton = button(text : "Choose", constraints : gbc(gridx: 1, gridy: 4), actionPerformed : showIncompleteChooser)
}
}
@Override
protected List<String> validate() {
// TODO Auto-generated method stub
return null
}
@Override
protected void apply(MuWireSettings muSettings, Properties i2pSettings) {
// TODO Auto-generated method stub
}
def showDownloadChooser = {
String text = chooseFile("Select directory for downloaded files")
if (text != null)
downloadLocationField.text = text
}
def showIncompleteChooser = {
String text = chooseFile("Select directory for incomplete files")
if (text != null)
incompleteLocationField.text = text
}
private String chooseFile(String title) {
def chooser = new JFileChooser()
chooser.setFileHidingEnabled(false)
chooser.setDialogTitle(title)
chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY)
int rv = chooser.showOpenDialog(null)
if (rv == JFileChooser.APPROVE_OPTION)
return chooser.getSelectedFile().getAbsolutePath()
else
return null
}
}

View File

@ -0,0 +1,34 @@
package com.muwire.gui.wizard
import com.muwire.core.Constants
import com.muwire.core.MuWireSettings
import com.muwire.core.util.DataUtil
class NicknameStep extends WizardStep {
volatile def nickField
public NicknameStep() {
super("nickname")
}
@Override
protected void buildUI(FactoryBuilderSupport builder) {
builder.panel(constraints : getConstraint()) {
label(text: "Select a nickname")
nickField = textField(columns: 30)
}
}
@Override
protected List<String> validate() {
if (!DataUtil.isValidName(nickField.text))
return ["Nickname cannot contain any of ${Constants.INVALID_NICKNAME_CHARS} and must be no longer than ${Constants.MAX_NICKNAME_LENGTH} characters. Choose another."]
null
}
@Override
protected void apply(MuWireSettings muSettings, Properties i2pSettings) {
muSettings.nickname = nickField.text
}
}

View File

@ -0,0 +1,22 @@
package com.muwire.gui.wizard
import com.muwire.core.MuWireSettings
abstract class WizardStep {
final String constraint
protected WizardStep(String constraint) {
this.constraint = constraint
}
protected abstract void buildUI(FactoryBuilderSupport builder)
/**
* @return list of errors, null if validation is successful
*/
protected abstract List<String> validate()
protected abstract void apply(MuWireSettings muSettings, Properties i2pSettings)
}