How to use actionlistener in Scala Swing - scala

I would like to make click counter in Scala Swing. I used for this ActionListener interface. But I don't know if I did it correctly. The program works, but I want to find out how to do this according to best practies. I will be very thankful for the answer how to do it correctly.
import javax.swing._
import java.awt._
import java.awt.event._
class UI extends JFrame {
var title_ : String = "Hello, Swing in Scala"
setTitle(title_)
val textArea = new JTextArea
var text : String = "Hello, Swing world in Scala!"
textArea.setText(text)
val scrollPane = new JScrollPane(textArea)
val panel = new JPanel
var text2 : String = "Click Here"
val button =new JButton(text2)
panel.add(button)
var clicks:Int = 0
def onClick(): Unit = {
clicks += 1
text = "Number of button clicks: " + clicks.toString
textArea.setText(text)
}
button.addActionListener(new ActionListener {
override def actionPerformed(e: ActionEvent): Unit = onClick() })
getContentPane.add(scrollPane, BorderLayout.CENTER)
getContentPane.add(panel, BorderLayout.SOUTH)
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE)
setSize(new Dimension(600, 400))
setLocationRelativeTo(null)
def display() {
setVisible(true)
}
}
object SwingExample extends App {
val ui = new UI
ui.display()
println("End of main function")
}

there are simple things, which improve code quality and maintainability.
it seems that text, text2 and title_ can remove and put that value directly into.
to shorten it, try this statement for the onClick
def onClick(): Unit = {
clicks += 1
textArea.setText(s"Number of button clicks: $clicks")
}
see String Interpolation.
you can shorten your ActionListener by
button.addActionListener(e => onClick())

Related

JavaFX in Scala too late repaint

I have a Scala Program with JavaFX GUI. When I fire a button, I disable my button and change a label text before calling a function that takes several tens of seconds to execute (RobotWebCore.runSearch()). The problem is that the GUI do not display the changes I bring to it before calling my function (but only after) :/
Here my code:
class GuiController extends Initializable {
#FXML
var GUI : VBox = null;
#FXML
var RQ_TEXT_FIELD : TextField = null;
#FXML
var SEARCH_BUTTON : Button = null;
#FXML
var STATE_LABEL : Label = null;
override def initialize(location : URL, resources : ResourceBundle ) = {
Printer.activeGui = true;
GuiController.instance = this;
SEARCH_BUTTON.setOnMousePressed(e => runSearch());
}
def setStateMessage(message: String) = STATE_LABEL.setText(message);
private def runSearch() = {
val p = ExpressionParser.LocalParser.parse(RQ_TEXT_FIELD.getText);
if (p.successful) {
GUI.setCursor(Cursor.WAIT);
SEARCH_BUTTON.setDisable(true);
setStateMessage("Recherche en cours");
RobotWebCore.runSearch(p.get);
SEARCH_BUTTON.setDisable(false);
GUI.setCursor(Cursor.DEFAULT);
} else {
setStateMessage("RequĂȘte invalide ! Exemple : (chat and chat) or perroquet");
}
}
}

How to pass variable from VaadinServlet to UI?

I'm new to Vaadin. I want to use it to build admin backend. As I am familiar DI (Guice), I would like to inject dependencies into servlet. However, I don't know how to pass from servlet to UI layer. Below is my sample codes:
#Singleton
#VaadinServletConfiguration(productionMode = false, ui = classOf[LoginUI])
class TestServlet #Inject()(userDAO: UserDAO) extends VaadinServlet with LazyLogging {
//How to userDAO to LoginUI?
override def servletInitialized(): Unit = {
}
}
#Theme("valo")
class LoginUI extends UI with LazyLogging {
override def init(request: VaadinRequest) = {
logger.debug("Init Login UI")
val content: VerticalLayout = new VerticalLayout
setContent(content)
val label: Label = new Label("Hello, world!")
content addComponent label
content addComponent new Button("Click Me!",
new ClickListener {
override def buttonClick(event: ClickEvent) =
Notification.show("The time is ")
})
}
}
I see that although it is UI, it acts like controller layer, so I think it does not anti-pattern when doing that. I find it even more difficult to inject things into UI. However, if I can inject directly into UI, it would be better.

How to signal/notify super-controller of change in sub-controller?

In JavaFX, how do you model the following:
I show a List of Customers in a Scene. On the left side there is a table on the right side (contentPane) the currently select customer's details are shown.
(Relevant part of) Main-Controller:
#jfxf.FXML
protected def newButtonPressed(): Unit =
{
contentPane.getChildren.clear
contentPane.getChildren.add(FXMLLoader.load(GUILoader.load("customers/form.fxml")))
}
There is a Button to add a new Customer. Upon clicking this button instead of opening a Popup, I replace the "details"-part of the scene and add a form there.
Now for this form (designed - like the rest of the GUI - in the SceneBuilder and saved as .fxml) I use another controller.
class Form extends Main with jfxf.Initializable
{
#jfxf.FXML
private var foreNameTextField: jfxsc.TextField = _
#jfxf.FXML
private var lastNameTextField: jfxsc.TextField = _
#jfxf.FXML
private var ageTextField: jfxsc.TextField = _
override def initialize(url: URL, resourceBundle: ResourceBundle): Unit =
{
}
#jfxf.FXML
protected def ok(): Unit =
{
// TODO validation
val newPerson = new Person(-1, foreNameTextField.getText, lastNameTextField.getText, ageTextField.getText.toInt)
// Save to DB
// Close whole form
}
}
When I'm done with filling in a new customer's detail I click on another button (that calls ok()) and save it to a database.
What I want to do now is close the form and replace it with the detail-form.
Something like calling a protected method in the main-controller like:
protected def clearDetails(): Unit =
{
contentPane.getChildren.clear
contentPane.getChildren.add(savedOldDetails)
}
won't work of course. (Will throw a runtime-exception because there is no contentpane in the sub/form-controller (even if I make it protected)
In Qt (C++) I'd use signals/slots and connect them accordingly.
Seems like in JavaFX there is nothing the like. How am I supposed to share such information?
Do I need to create a "super-controller" for the contentPane?
(I don't know Scala, so I'll write this in Java, hope that is ok).
You can define an observable property in the Form controller, and observe it when you load the form from the main controller:
public class Form implements Initializable {
private final ObjectProperty<Person> person = new SimpleObjectProperty<>(null);
public ObjectProperty<Person> personProperty() {
return person ;
}
public final Person getPerson() {
return personProperty().get();
}
public final void setPerson(Person person) {
personProperty().set(person);
}
// ... code you had before...
#FXML
protected void ok() {
Person person = new Person(-1, foreNameTextField.getText(), lastNameTextField.getText(), ageTextField.getText());
// save to DB...
personProperty().set(person);
}
}
Now in your main controller, load the form as follows:
#FXML
protected void newButtonPressed() {
contentPane.getChildren().clear();
FXMLLoader loader = new FXMLLoader(getClass().getResource("customers/form.fxml"));
Parent form = loader.load();
Form formController = loader.getController();
formController.personProperty().addListener((obs, oldPerson, newPerson) {
if (newPerson != null) {
// clear form, etc, e.g.
clearDetails();
}
});
contentPane.getChildren().add(form);
}

Keep the state or value of a DropDownChoice and TextField both inside a ListView

I have implemented a listView and every time that a new item is added in this ListView the values inserted in the textField and selected in a DropDownChoice are lost.
The following pictures show what's happen before and after add a new ListView item:
And after Add a new item:
I'm implemented the code bellow:
var listData = workSheetDAO.listWorkSheetUser(selectedUser, beginDate, endDate)
var lbPeriodTotal = new Label("periodTotal")
lbPeriodTotal.setOutputMarkupId(true)
add(lbPeriodTotal)
add(new ListView[WorkSheet]("listWorkSheet", listData) {
override protected def onBeforeRender() {
super.onBeforeRender()
}
def populateItem(item: ListItem[WorkSheet]) = {
var workSheet = item.getModelObject()
item.add(new LinkDate("initDate", workSheet, 1))
item.add(new LinkDate("endDate", workSheet, 1))
item.add(new TextField("description"))
val listCustomer: java.util.List[Customer] = customerDAO.listCustomers
item.add(new DropDownChoice("customerSelection", listCustomer, new ChoiceRenderer[Customer]("id")))
if (workSheet.endDate == None) {
item.add(new Label("total", ""))
} else {
var period = new Period(workSheet.initDate, workSheet.endDate.get)
periodTotal = periodTotal.plus(period)
lbPeriodTotal.setDefaultModel(new Model(periodTotal.toPeriod().normalizedStandard().toString(getFormatter())))
period = period.normalizedStandard()
item.add(new Label("total", period.toString(getFormatter())))
}
}
}
private class LinkDate(id: String, workSheet: WorkSheet, type: Int) extends Link[String](id) {
setEnabled(false)
add(new Label("label", new Model[String]() {
override def getObject(): String = {
var result = ""
if (type == 1) {
result = workSheet.initDate.toString("dd/MM/YYYY HH:mm:ss")
} else
result = workSheet.endDate.toString("dd/MM/YYYY HH:mm:ss")
return result
}
}))
def onClick() {}
}
The only values preserved are the values of the labels.
Please, someone could help me, thanks
After you add new item to the List you refresh it, so all not saved values will be lost.
In your case would do the following:
First, save data on change (onblur/onchange or with button 'save').
Second, bind description and customerSelection on populating, like so:
new TextField("description", new PropertyModel(obj, "description"))
new DropDownChoice("customerSelection", new PropertyModel(obj, "customer"), listCustomer, new ChoiceRenderer[Customer]("id")))

Incorporate fields from MegaProtoUser into a wizard screen

I'm just starting out with scala and lift and am a bit stuck regarding incorporating form fields from MegaProtoUser into a wizard screen.
I have the following MegaProtoUser code:
object User extends User
with MongoMetaRecord[User]
with MetaMegaProtoUser[User] {
override def skipEmailValidation = true
}
class User private () extends MongoRecord[User]
with MegaProtoUser[User] {
def meta = User
//protected methods ...
}
And my wizard setup looks like the following:
object SignupWizard extends Wizard {
object completeInfo extends WizardVar(false)
val person = new Screen {
//Incoroporate MegaProtoUser fields here ...
override def nextScreen = {
business
}
}
val business = new Screen {
val business = field("Business name","")
}
def finish() {
S.notice("Thank you for registering!")
completeInfo.set(true)
}
}
I want to incorporate some selected (not all) fields from my MegaProtoUser class into the person screen but do not now how to do this with the methods available to both. Any help is much appreciated.
AbstractScreen's addFields method can add individual fields, as well as adding all the fields from a Record object. This works on both LiftScreen and Wizard's Screen.
// assuming you just want to create a new user
object user extends WizardVar(User.createRecord)
val person = new Screen {
// addFields(() => user.is) would add all fields
addFields(() => user.is.email)
addFields(() => user.is.password)
addFields(() => user.is.timezone)
override def nextScreen = {
business
}
}