You will learn:
- how Pharo helps the "Program by intention" style of programming
- how to create basic persistency of created Contact objects
- how to compose a GUI, basic widgets, events and layout
Download screencast (1280x768): .mov 75.2 MB
View mobile version.
More Polymorph in Picasa screencast.
Get the code:
Gofer it
squeaksource: 'Pharocasts';
package: 'ContactManager';
load.
(Smalltalk at:#ContactListEditor) open.
Start by looking how the contact list is built:
ContactListEditor>>open
|builder content|
builder := UITheme builder.
content := builder newColumn: {
builder
newListFor: self
list: #contacts
selected: #contactSelectedIndex
changeSelected: #contactSelectedIndex:
help: 'contacts'.
builder newRow: {
builder newButtonFor: self
action: #addButtonClick
label: 'Add'
help: 'Create a new contact'.
builder newButtonFor: self
action: #removeButtonClick
getEnabled: #hasSelectedContact
label: 'Remove'
help: 'Remove selected contact'.
builder newButtonFor: self
action: #editButtonClick
getEnabled: #hasSelectedContact
label: 'Edit'
help: 'Edit selected contact' }}.
(content openInWindowLabeled: 'Contacts') extent: 400@500.
#newRow: and #newColumn: are an easy way to align elements on the window.When the Add button is clicked, message #addButtonClick is sent on the ContactListEditor object:
ContactListEditor>>addButtonClick
|newContact|
newContact := Contact new.
ContactEditor new
contact: newContact;
onOK: [ Contact database add: newContact.
selectedContactIndex := Contact database size.
self
changed: #contacts;
changed: #hasSelectedContact];
openModal.
The closure given to #onOK: adds the new Contact and tells the view to refresh components which depends on #contacts and #hasSelectedContact selectors - that means the contact list and the Remove and Edit buttons.ContactEditor defines a modal dialog to edit the firstName and lastName of a Contact:
ContactEditor>>openModal
|builder dialog content firstName|
builder := UITheme builder.
content := (builder newLabelGroup: {
'First name' -> (
firstName := (builder
newTextEntryFor: contact
getText: #firstName
setText: #firstName:
help: 'Enter the first name of the contact')
acceptOnCR: false;
minWidth: 200).
'Last name' -> (
(builder
newTextEntryFor: contact
getText: #lastName
setText: #lastName:
help: 'Enter the last name of the contact')
acceptOnCR: false;
minWidth: 200) }).
dialog := builder
newPluggableDialogWindow:'Edit contact'
for: content.
dialog rememberKeyboardFocus: firstName.
builder openModal: dialog.
dialog cancelled ifFalse: [self doOnOK].
From Gary Chambers (and thanks !):Disabling the acceptOnCR for each text frield allows the default dialog handling for the return key (defaults to OK).
Normally the initial keyboard focus for a dialog is the default button, if specified. Remembering the first name field prior to opening will give that field focus.
Now it should be easier to understand Polymorph examples found in
UITheme class>>exampleBasicControls and friends (in examples protocol).
Thanks Laurent! I really appreciate your videos. They are very helpful and give enough information to help me to make progress in my Smalltalk code. Keep up the great work please!
ReplyDeleteGreat work! Please continue with videos like this.
ReplyDeleteI am the type of programmer who wants to make GUI mainly for database records and this is what I was seeking for. Lack of documentation (at least telling where to start and showing the basics ... the rest we can read from the code) is a huge break from starting developing in Smalltalk for many of developers.
If I inspect "ContactListEditor allInstances" it shows an empty array.
ReplyDeleteIf I then doit "ContactListEditor open" twice (creating 2 instances), and inspect "ContactListEditor allInstances" again, it shows an array with two entries, as expected.
But if I close one (or both) of the "Contacts" windows, then inspect "ContactListEditor allInstances" again, it still shows two instances.
Are the instances still somewhere and accessible? If not, is there any way to make "ContactListEditor allInstances" report correctly?
I could not reproduce it. Which version of Pharo ? VM ? OS ?
ReplyDeleteMy "ContactListEditor allInstances" question appears to be related to garbage collection. The instances are in the array until the system cleans up.
ReplyDeleteButton questions:
ReplyDelete1. How do you add a button to the Add/Edit/Remove row that will close the ContactListEditor window? I tried adding
builder newButtonFor: self
action: #closeButtonClick
label: 'Close'
help: 'Close this window'.
with a new method
closeButtonClick
self delete.
but that generates a MNU error.
---
2. How do you code a button that takes one or more parameters when the button is clicked?
1. ContactListEditor is not a Window but an Object subclass.
ReplyDeleteThe window is created by #openInWindowLabeled:. So in ContactListEditor>>open
....
mainWindow := (content openInWindowLabeled: 'Contacts') extent: 400@500.
and in
ContactListEditor>>closeButtonClick
mainWindow delete
2. What do you want to do ? You can define a new button class which do it but may be overkill.
Can you explain how changes (via Edit button) are saved? The addButtonClick method uses onOK:aBlock, but editButtonClick doesn't.
ReplyDeleteIn ContactEditor>>openModal, Contact objects are associated to TextEntries. So on OK the data in the fields are passed to #setText: selectors (whether it's an add or edit - ContactEditor doesn't know the workflow).
ReplyDeleteThe onOK: block is just used to add the Contact object to the database. In case of an edit, the edited Contact is already in the database so there's nothing to do.
I would like to see this type of sample application updated with a connection to a Magma instance to demonstrate how to start and connect to Magma
ReplyDeleteNelson, have you seen http://www.pharocasts.com/2010/02/magma-object-oriented-database.html ?
ReplyDeleteWatching now! thank you very much
ReplyDelete