Mint azt már korábban az osztályok és metaosztályok tárgyalásakor említettük, a Smalltalk rendszerben minden, még az osztályok is objektumok. Az osztályok közös őse az Object osztály, így minden objektum, tehát minden entitás a Smalltalk rendszerben az Object osztály egy példánya. Az Object osztályban definiált műveletekkel tehát minden Smalltalk objektum rendelkezik. Az Object osztály fontosabb metódusai kategóriájuk szerint a következők:
Egy objektum funkcionalitását az osztálya határozza meg. Az objektumok funkcionalitását kétféleképpen ellenőrizhetjük: annak vizsgálatával, hogy az adott objektum példánya-e (közvetve, vagy közvetlenül) egy osztálynak, vagy hogy az objektum rendelkezik-e egy megadott művelettel:
class
az üzenet fogadójának
osztályát reprezentáló objektumot adja vissza. Például:
#(1 kettő 3) class "--> Array " 3 class "--> SmallInteger " 3 class class "--> SmallInteger class " 3 class class class "--> Metaclass "
isMemberOf: aClass
eldönti, hogy az
üzenetet fogadó objektum példánya-e közvetlenül az aClass
osztálynak (azaz self class == aClass -- lásd
összehasonlítás). Például:
#(1 2 3) isMemberOf: Array "--> true " #(1 2 3) isMemberOf: ArrayedCollection "--> false " 'hello' isMemberOf: String "--> true " 'hello' isMemberOf: ArrayedCollection "--> false "
isKindOf: aClass
eldönti, hogy az
üzenetet fogadó objektum példánya-e (közvetlenül vagy
közvetve, leszármazás útján) az aClass osztálynak. Az előző
példáknál maradva:
#(1 2 3) isKindOf: Array "--> true " #(1 2 3) isKindOf: ArrayedCollection "--> true " 'hello' isKindOf: String "--> true " 'hello' isKindOf: ArrayedCollection "--> true " 'hello' isKindOf: Number "--> false "
respondsTo: aSymbol
eldönti, hogy az
üzenetet fogadó objektum rendelkezik-e aSymbol nevű metódussal.
Például:
3 < 5 respondsTo: #ifTrue: "--> true " 3 respondsTo: #ifTrue: "--> false " 3 < 5 respondsTo: #respondsTo: "--> true " 3 respondsTo: #respondsTo: "--> true "
Lévén, hogy a Smalltalk rendszerben minden információ objektumként jelenik meg, szükség van az objektumok összehasonlítására. A Smalltalk az Object osztály szintjén kétféle összehasonlítást ismer: az ekvivalencia és az egyenlőség vizsgálatát. Az ekvivalencia fogalma osztályfüggetlen, az egyenlőség osztályonként különbözőképen értelmezhető. Két objektum ekvivalens, ha megegyeznek és egyenlőek, ha ugyanazt a komponenst reprezentálják. Az Object osztály összehasonlítást végző metódusai a következők:
== anObject
eldönti, hogy az üzenet fogadója és anObject ugyanaz az
objektum-e vagy sem.
= anObject
eldönti, hogy az üzenet fogadója és anObject ugyanazt a
komponenst reprezentálják-e vagy sem.
~~ anObject
az
== anObject üzenettel ellentétes választ ad.
~= anObject
az
= anObject üzenettel ellentétes választ ad.
isNil
eldönti,
hogy az üzenetet fogadó objektum a nil objektum-e vagy sem (==
nil).
notNil
az
isNil üzenettel ellentétes választ ad. (~~ nil)
hash
egy egész értéket ad. A hash
érték az objektum hash-táblában tárolásakor játszik
szerepet, de annyiban kapcsolódik az összehasonlításhoz, hogy
követelmény az, hogy az egyenlő objektumok hash értéke is
egyenlő legyen.
Példák:
a := Set new add: 1; add: 2; yourself. b := Set new add: 1; add: 2; yourself. a == b "--> false " a = b "--> true " d := b. d == b "--> true " d = b "--> true " a hash "--> 2588 " 12345 hash "--> 12345 " 1234567890 hash "--> 18898 " 1234567890 hash hash "--> 18898 "
az alkalmazásokban gyakran kell egy-egy objektumról másolatot készíteni. A másolás az egyenlőséghez hasonlóan többféleképpen értelmezhető, az osztálytól, a komponensek reprezentációjától függően. Bizonyos esetekben szükség lehet még egy objektum felszíni, vagy épp ellenkezőleg minden részére kiterjedő másolására. Az Object osztály a következő objektummásoló metódusokat definiálja:
shallowCopy
a
fogadó objektum egy felszíni másolatát adja vissza. A fogadó
objektum példányváltozói nem másolódnak le, a másolattal
közösen ugyanazokra az objektumokra hivatkoznak.
deepCopy
a
fogadó objektum egy teljes másolatát adja vissza. A fogadó
objektum példányváltozói is lemásolódnak.
copy
osztályonként más és más, az
osztály alapértelmezett objektum másoló metódusa. Az Object
osztályban a shallowCopy-val van implementálva. Felüldefiniálható
úgy is, hogy deepCopy-t hajtson végre, vagy akár egy köztes,
részben felszíni részben teljes másolást.
A shallowCopy és a deepCopy közti különbséget az alábbi ábra szemlélteti:
Példák:
a := #(1 'ketto' 3) b := a copy a = b "--> true " a == b "--> false "
A Smalltalk egy sajátossága az indexelt példányváltozók támogatása. Minden objektumank lehetnek indexelt változói a nevesített példányváltozói mellett. Az Object osztály a következő metódusokat definiálja az indexelt változók elérésére:
at: index
a
fogadó objektum index-edik indexelt példányváltozóját adja
vissza, vagy egy hibaüzenetet generál, ha az objektumnak
nincsenek indexelt példányváltozói, vagy ha az objektum
index-nél kevesebb indexelt példányváltozóval rendelkezik.
at: index put: anObject
a fogadó objektum index-edik indexelt példányváltozójába
tárolja a megadott anObject objektumot, amely egyben a metódus
visszatérési értéke is. Az üzenet küldése most is
hibaüzenetet vált ki, ha a fogadó objektumnak nincsenek indexelt
példányváltozói, vagy ha a fogadó objektum index-nél kevesebb
indexelt példányváltozóval rendelkezik.
size
visszaadja
a fogadó objektum indexelt példányváltozóinak számát. Az
indexelt példányváltozók 1-tol size-ig vannak indexelve.
basicAt: index basicAt: index put:
anObject basicSize
megegyeznek az at:,
at:put: size, metódusokkal de nem definiálhatók felül a
leszármazott osztályokban, következésképen mindig
rendelkezésre állnak, tehát az at:, at:put: size metódusok
felüldefiniálásakor is számíthatunk az implementációjukra.
Példák:
abc := #(a b c d e f g) abc size "--> 7 " abc at: 5 "--> e " abc at: 1 put: #A "--> A " abc "--> (A b c d e f g) "
Grafikus felület ide vagy oda, nélkülözhetetlen az objektumok szöveges reprezentációja. A Smalltalk Object osztálya megkülönbözteti az emberi olvasásra szánt áttekinthető szöveges reprezentációt a gép számára könnyen feldolgozható precíz leírástól, amely alapján az eredeti objektum könnyen megkonstruálható. Ezzel kapcsolatban a következő metódusokat érdemes kiemelni:
printString
a
fogadó objektum áttekinthető leírását tartalmazó String
objektumot ad eredményül.
printOn: aStream
a megadott aStream objektum végéhez fűzi a fogadó objektum
áttekinthető leírását.
storeString
a
fogadó objektum precíz leírását tartalmazó String objektumot
ad eredményül, amelyből az eredeti objektum megkonstruálható.
storeOn: aStream
a megadott aStream
objektum végéhez fűzi a fogadó objektum precíz leírását.
Példák:
halmaz := Set new add: 1; add: 2; add: 3; yourself. abc := #(a b c d e f g) halmaz printString --> 'a Set(1 2 3)' halmaz storeString --> '((Set new) add: 1; add: 2; add: 3; yourself)' abc printString --> '#(#a #b #c #d #e #f #g)' abc storeString --> '#(#a #b #c #d #e #f #g)'
Tekintve, hogy a Smalltalk rendszerben fordítási időben csak szintaktikai elemzés történik, a szemantikai hibák kezelésére futási időben kell, hogy sor kerüljön. A leggyakrabban előforduló hiba az, amikor egy objektum nem a feltételezett osztály egy példánya. Ez úgy jelentkezik, hogy előbb utóbb a kritikus objektum olyan üzenetet kap, amelyet nem tud értelmezni, nincsen annak megfelelő metódusa. A hibás helyzetek kezelésére az alábbi metódusokat definiálja az Object osztály:
doesNotUnderstand:
aMessage
jelzi a felhasználónak, hogy a fogadó
objektum a megadott aMessage üzenetet nem tudja értelmezni.
error: aString
szabványos hibajelzést küld a felhasználónak, amelyben a
megadott aString hibaüzenet tájékoztatásként szerepel.
primitiveFailed
hibajelzést küld a felhasználónak, hogy egy
rendszerprimitívvel implementált metódus végrehajtása során
hiba történt.
shouldNotImplement
jelzi a felhasználónak, hogy olyan metódus hívódott meg,
amelyet nem lett volna szabad felüldefiniálni.
subclassResponsibility
jelzi a
felhasználónak, hogy a fogadó objektum ősosztályának olyan
"absztrakt" metódusa hívódott meg, amelyet az objektum
osztályának felül kellett volna definiálnia.
A Smalltalk igen kiterjedt osztályhierarchiával rendelkezik, és az eddigiekben is sok osztályt bemutattunk már, így most csak a legfontosabb elemekre térünk ki. A beépített könyvtár metódusaiban mindig találunk egy rövid leírást a metódusról, gyakran a metódus kódja is csak más Smalltalk metódusokra hivatkozik, így abból is kitalálhatjuk az eljárás célját. Lehetőség van arra is, hogy megkerestessük azon metódusokat, ahol hivatkoznak erre az eljárásra, így gyakran a felhasználás módjára is kapunk példát.
A leszármazási sor jelölésére az alábbiakban a következő
jelölést használjuk: Ős~Leszármazott. Tehát például:
Object~Magnitude~Number~Integer~SmallInteger
A hierarchia csúcsán az Object osztály áll, amelyben definiálták például az egyenlőség(=) és az azonosság (==) műveletét, az indexelt adattagok kezeléséhez szükséges metódusokat, típusvizsgálatokat.
Az Object~Behavior osztályban szerepel a Class és a MetaClass osztály.
Az Object~Boolean osztályban két külön osztályt képez a True és a False. A Smalltalkban máshol is megfigyelhető az a módszer, hogy bizonyos, az osztályhoz tartozó dolgokat inkább minden leszármazottban külön-külön definiáljunk, mert ott azok sokkal egyszerűbben megvalósíthatók. A Boolean esetében például az ifTrue szerkezet a True osztályban a blokkot mindenképpen végrehajtja, a False osztályban pedig, egyszerűen üres utasítás. Van két pszeudováltozónk, true a True és false a False osztály egyetlen példánya.
Az Object~Magnitude osztályban szerepelnek a számtípusok (például racionális számok, Object~Magnitude~Number~Fraction), karakterek, dátum és idő, valamint az Association osztály, amely (kulcs,érték) párok tárolásával asszociatív adatszerkezet megvalósítására szolgál.
Az Object~Collection osztály szolgál az összetett adattípusok megvalósítására. Három alosztály a Bag, a Set és az IndexedCollection.
Az Object~Collection~Set osztály leszármazottja a Dictionary osztály, amely segítségével például az osztályok poolDictionaries részét adhatjuk meg.
Az Object~Collection~IndexedCollection osztály közvetett leszármazottja a FixedSizeCollection~Array, és néhány olyan osztály, amelyeknek jelentésük szerint nagyon máshova kellett volna kerülnie, felépítésük miatt mégis ide kerültek. Például a FixedSizeCollection~ByteArray~FileHandle, vagy az OrderedCollection~Process, amelynek osztályszintű metódusai között interrupt kezelő eljárások is szerepelnek.
Mivel a Smalltalkban minden objektum, így a fordító is. Osztálya az Object~Compiler.
A blokkok reprezentálására az Object~Context, illetve leszármazottja a HomeContext használható. (Bizonyos implementációkban e helyett a BlockClosure osztályt használják.)
File-ok kezeléséhez használható az Object~File és az Object~Directory osztály, amelyek tehát teljesen különálló osztályok, és bőséges metóduskészletet kínálnak a file-ok és könyvtárak kezelésére.
Az Object~Dos osztály példányaival közvetlenül elérhetjük a regisztereket, interruptokat válthatunk ki, stb.
A Smalltalk támogatja Windows-os programok létrehozását, így kezelhetünk ablakokat (Object~Window, Object~Win, ...), menüket (Object~Menu, Object~MenuItem), betűtípusokat (Object~Font).
File-ok adatfolyamként való kezelését valósíthatjuk meg a Object~Stream osztály segítségével.
Az Object~UndefinedObject osztály egyetlen példánya a nil.
Fogalmi szempontból egy gyűjtemény (collection) nem más, mint bizonyos értékek halmazának vagy sorozatának enkapszulációja önálló értékként (objektumként). Ezen objektum tárolójaként funkcionál a befoglalt objektumok halmazának / sorozatának.
Az Array, String, Set, Dictionaries (Map) osztályok mind Collections-ként vannak implementálva a Smalltalk-ban.
Működése szempontjából egy gyűjtemény azon objektum, amely reagál a felé érkező azon alap üzenetekre, melyek biztosítják a "gyűjtemény viselkedést".
Általában - bár nem szükségképpen - ez azt jelenti, hogy egy "Collection objektum" példánya egy olyan osztálynak, mely a standard Collection osztályból származik.
Az alapvető "gyűjtemény üzenet" a
#do:, melynek fogadója egy gyűjtemény és argumentuma egy egy-argumentumos blokk. Válaszként a #do: üzenetre a gyűjtemény végigiterál minden értéken, melyet a gyűjtemény elemként tárol és kiértékeli rájuk az egy-argumentumos blokkot. (Ezen blokk a #do: üzenet argumentuma)
Így minden elemet egyesével beilleszt a blokkutasítás argumentumába, mely elementként végrehajtódik.
Néhány példa:
A példa kiszámítja az értékek összegét, melyek elemei egy tömb literálnak, és beállítja a sum változó értékét ezen összegre.
=>108
A példa kiírja egy String minden karakterét egy Stream-be.
A példa visszatérési értékként a minValue változóba számítja egy tömb elemei közül azon értéket, mely az összes többi értéknél kisebb vagy egyenlő.
=>-192
A #do: üzenet mellett más széles körben használt gyűjtemény üzenetek is léteznek, melyek szintén a tartalmazott elemeken mennek végig.
Ezek a #select:, #reject:, #collect:, #contains:, #detect:ifNone: and #inject:into: üzenetek, melyek mind-mind implementálhatók csupán a #do: üzenet segítségével is. Eképp vannak implementálva magában a Smalltalk standard Collection osztályában is. (minden Collection-ből származtatott osztály is rendelkezni fog tehát ezen műveletekkel alapértelmezetten.)
Példa a használatukra, magyarázattal melyik pontosan mit is csinál:
Message pattern:
aCollection select: aOneArgBlock
#select: válaszként visszaad egy új gyűjteményt (általában azonos típusút, mint a fogadó), amely elemei kielégítik a megadott megszorítást. Ezen állítás (mely egy funkció ami egy vagy több argmunetummal Boolean értékre értékelődik ki) egy egy-argumentumú blokkal van reprezentálva, ami kiértékelődik minden egyes elemére a fogadó objektumnak, mint blokk argumentum. Értéke minden "lépésben" true vagy false. A gyűjtemény csak azon elemekkel tér vissza, melyek true-ra értékelik ki a #select: argumentumaként adott logikai kifejezést.
=> #(-35 -192)
Message pattern:
aCollection reject: aOneArgBlock
#reject: A #select: inverz művelete, ebben az esetben a válasz egy olyan új gyűjtemény, melynek elemei csak azon elemek az eredeti gyűjteményből, melyek nem elégítik ki az argumentumként (egy-arugmentumos blokk formájában) megadott feltételt.
=> #(51 6 278)
Message pattern:
aCollection collect: aOneArgBlock
#collect: válaszként visszaad egy új gyűjteményt (általában azonos típusút, mint a fogadó), mely úgy konstruálódik, hogy a #collect: argumentumaként adott egy-argumentumos blokk kiértékelődik a gyűjtemény minden egyes elemére. A régi értékek természetesen nem kerülnek módosításra, az eredményként kapott elemek az új gyűjteménybe kerülnek.
=> 'STRINGS ARE COLLECTIONS, TOO!'
Message pattern:
aCollection contains: aOneArgBlock
#contains: válaszként megadja vajon a gyűjtemény tartalmaz-e olyan elemet, melyre az argumentumként megadott egy-argumentumos blokk true-ra értékelődik ki.
=> true
Message pattern:
aCollection detect: aOneArgBlock ifNone: noArgBlock
#detect:ifNone: válaszként megadja az első olyan elemét a fogadú gyűjteménynek, melyre az első argumentumként megadott (a #detect: kulcsszót követő) egy-argumentumos blokk true-ra értékelődik ki. Ha nincs a feltételt teljesítő érték az elemek között, akkor az argumentummal nem rendelkező blokk értékelődik ki és tér vissza.
=> 'ftp://elsie.nci.nih.gov/pub/'
Message pattern:
aCollection inject: anObject into: aTwoArgBlock
#inject:into: válasza egy olyan érték, mely a következő eljárás hatására jön létre:
A két-argumentumú blokk (mely az #into: kulcsszót követi) két argumentuma közül az első az #inject: kulcsszó után megadott argumentum értékét veszi fel, míg a második a fogadó gyűjtemény első elemének értékét. Majd a két-argumentumú blokk kiértékelődik, ezen érték lesz a következő lépésben a blokk első argumentuma, míg a fogadú gyűjtemény következő elem a második argumentuma. A végső blokk kiértékelés lesz végül maga a visszatérési érték.
Ez az üzenet általánosan arra használt, amire a példa is utal: kiszámítja egy gyűjtemény elemeinek összegét.
(Bár sokkal több mindenre is felhasználható.)
=> 122
Vannak még további általánosan elterjedt üzenetek, melyeket gyűjteményeknek küldhetünk - bár néhány gyűjtemény-típus nem feltétlenül tud hozzájuk rendelni valódi jelentéssel bíró választ:
Message pattern:
aCollection size
#size válaszként megadja a fogadó gyűjtemény által tartalmazott elemek számát.
=> 39
Message pattern:
aCollection at: aKeyOrIndex
at: válaszként megadja a fogadú gyűjtemény azon elemét, melyet a megadott index vagy kulcs azonosít. (Szintaktikailag nincs különbség a két fogalom közt, de a megvalósításban, illetve a háttérszerkezetben igen nagy eltérések lehetnek!)
A fogadó vagy egy SequenceableCollection (e.g., a String) vagy egy KeyedCollection (e.g, a Dictionary) kell legyen.
=> 'two'
Message pattern:
aCollection at: aKeyOrIndex put: anObject
at:put: a #put: kulcsszót követő argumentumot letárolja, mint a fogadú gyűjtemény egy elemét, beillesztve azt az #at: argumentum által megadott index vagy kulcs pozícióra. (Szintaktikailag nincs különbség a két fogalom közt, de a megvalósításban, illetve a háttérszerkezetben igen nagy eltérések lehetnek!)
Az üzenet visszatérési értéke a #put: kulcsszót követő argumentum értéke lesz.
A fogadó vagy egy SequenceableCollection (e.g., a String) vagy egy KeyedCollection (e.g, a Dictionary) kell legyen.
=> 'Flintstone'
Message pattern:
aCollection add: anObject
"#add: letárolja az argumentumként kapott objektumot, mint a fogadú gyűjtemény új elemét, az utolsó pozícióra illesztve azt rendezett esetben. Ha a fogadó halmaz szemantikát követ, akkor az új elem csak akkor kerül be a gyűjteménybe, ha értéke még nincs jelen. Az üzenet visszatérési értéke maga az argumentum értéke lesz.
A fogadó nem lehet egy KeyedCollection (e.g, a Dictionary), de növelhető Collection-nek kell lennie (e.g, not an Array or String).
=> 42
A Smalltalk tervezésekor fontos szempont volt, hogy a nyelv egy interaktív, grafikus fejlesztői környezetet foglaljon magába. A nyelv megalkotói ennek megvalósítására fejlesztették ki a manapság elterjedt, ablakokat, ikonokat, menüket és egeret használó (WIMP – Window-Icon-Menu-Pointer) grafikus felület elődjét. Ez a grafikus modell azóta alapjában véve nem változott, de bizonyos részei implementációfüggőek, így a lent bemutatott beépített osztályok az egyes Smalltalk verziók esetén kisebb részletekben eltérhetnek, illetve új osztályokkal és szolgáltatásokkal egészülhetnek ki.
A Smalltalk grafikus modellje a képeket kétdimenziós tömbként (kezdetben még csak kétszínű kijelzők voltak, így bittömbként) fogja fel, melyben az egyes elemek a megfelelő pixel színét jelentik. A modell széleskörű grafikus eszköztárat nyújt, melyben a következőkre van lehetőségünk:
Képek rajzolása: Az egeret különböző alakú, textúrájú és méretű ecsetként használva a képernyőre rajzolhatunk képeket. Ezenkívül a képeket, mint bitképeket is szerkeszthetjük, közvetlenül a kétdimenziós tömböt módosítva.
Egyszerű animációk: Egyszerű mozgóképeket hozhatunk létre, ha egy adott képet dinamikusan módosítunk, vagy megjelenítünk egy képet, mialatt a következő képkockát előállítjuk (azaz dupla bufferelésre is lehetőségünk van, ld. a DisplayScreen osztálynál).
„Teknős grafika”: A Smalltalkban megtalálhatóak a LOGO nyelvben megszokott funkciók is.
Szöveg megjelenítése: Smalltalkban a szövegeket több különféle betűtípussal, méretben, stílusban jeleníthetjük meg.
Vonalak és görbék rajzolása: Lehetőségünk van különböző geometriai alakzatok, például körök, körívek, szakaszok, spline-ok kirajzolására is.
A Point osztály kétdimenziós pontok, a Rectangle osztály kétdimenziós téglalapok leírására szolgál. Az egyes pixelek egy képen, vagy a képernyőn belül a Point osztály példányai segítségével érhetők el. Hasonlóan, pixelek téglalap alakú területének elérésére a Rectangle osztály objektumait használjuk.
A Smalltalk koordinátarendszerében az origó a bal felső sarokban található, az x-tengely jobbra nő, míg az y-tengely pozitív iránya a lefelé irány.
Pontok létrehozására külön nyelvi elem is van a Smalltalkban: a @ bináris operátor segítségével egy új pont objektumot hozhatunk létre. Ugyanerre szolgál az osztály #x: #y: üzenete is. Ügyeljünk arra, hogy a Point mindössze egy rendezett pár, létrehozáskor a paraméterekre semmilyen ellenőrzés nem hajtódik végre, vagyis az egyes koordináták típusa bármi lehet (például Point, sőt, akár egy blokk objektum is). Ezenkívül az Integer objektumokat az #asPoint művelet segítségével konvertálhatjuk Point-ra, az eredmény egy olyan pont lesz, melynek mindkét koordinátája megegyezik az Integer objektum értékével.
Téglalap létrehozására több lehetőségünk van. A leggyakrabban használt konstruktorban (#corner: #extent: ) a téglalap bal felső sarkát, és az oldalak hosszát adjuk meg, Point objektumokként. Itt is ügyeljünk arra, hogy a Rectangle objektumok is mindössze rendezett párok, vagyis létrehozáskor semmiféle ellenőrzést nem végez a rendszer a paraméterekre, nem vizsgálja, hogy azok valóban Point típusú objektumok-e. Hasonlóan azt sem ellenőrzi, hogy a téglalap érvényes-e, azaz a bal felső sarka nincs-e a jobb alsó sarok alatt, vagy tőle jobbra. Új téglalap létrehozására lehetőségünk van még az origó (azaz a téglalap középpontjának) és a bal felső sarok, az origó és a méretek, illetve a bal, jobb, felső, alsó koordináták megadásával is. Ezenfelül, interaktív módon is létrehozhatunk téglalap objektumokat. A #fromUser hatására a rendszer a felhasználótól várja a téglalap határainak kijelölését a képernyőn, míg az #originFromUser: extentPoint üzenet hívásával a téglalap origóját kell kijelölnünk, a méretet pedig az extentPoint határozza meg. Az előbbi műveleteket kiegészíthetjük egy Point típusú grid paraméterrel, mely azt határozza meg, hogy a téglalap határainak milyen felbontású rácsra kell esnie, azaz a felhasználó által kijelölt téglalap csúcsait a legközelebbi rácspontokhoz igazítja, melyek koordinátái a grid koordinátáinak többszörösei. A már meglévő téglalapok határainak módosítására is van lehetőség, valamint lekérdezhetjük a csúcsait, oldalainak felezőpontjait, illetve a területét.
A pont és téglalap objektumok kezelésére a nyelv rengeteg hasznos műveletet biztosít. A Point objektumokra definiált a legtöbb aritmetikai művelet, azaz a +, -, *, /, // és az abszolút érték (#abs), melyek mindegyike koordinátánként hajtódik végre. Ezenkívül az előbbi műveletek pontok és egész számok között is értelmezve vannak. Pontok között az összehasonlító operátorok is definiáltak, egy pont akkor kisebb egy másik pontnál, ha tőle balra és felfelé található (vagyis mindkét koordinátája kisebb). Hasznos lehet a pontok polárkoordinátás alakjának meghatározása is, a Smalltalk erre az #r és a #theta üzeneteket biztosítja, előbbi a helyvektor hosszát (a pont origótól való távolságát), utóbbi az x tengellyel bezárt szöget adja meg radiánban. A Point osztályban még néhány hasznos műveletet találunk: két pont távolsága, az objektumnak két másik pont által meghatározott szakaszhoz legközelebbi pontja, skaláris szorzat, normálvektor, valamint a pont irányába mutató egységvektor meghatározása, melyek rendre #dist: , #pointNearestLine: #to: , #dotProduct: , #normal, és #unitVector.
Mivel egy interaktív, ikonokat, menüt és egeret használó grafikus felületen annak meghatározására, hogy a felhasználó milyen műveleteket hajtott végre, mikre kattintott rá, miket jelölt ki, leginkább pontok és téglalapok közötti viszonyt megállapító műveletek szükségesek, a Smalltalk rengeteg ilyen metódust definiál. Lehetőségünk van például meghatározni, hogy egy téglalap tartalmaz-e egy másik téglalapot (#contains: ) vagy egy pontot (#containsPoint: ), két téglalap metszi-e egymást (#intersects: ), meghatározni a metszeteket, a különbségeket stb. Ezen felül, szintén elsősorban az interakció támogatására számos művelet áll rendelkezésünkre téglalapok és pontok transzformálására (pl. skálázásra, eltolásra, stb).
Smalltalkban a képeket a Form osztály objektumai reprezentálják, melyek többek között a kép pixeleinek leírására használt kétdimenziós tömböt tárolják. A képeken végzett műveletek leírására külön osztályt, a BitBlt-t vezették be. Az elnevezés a BitBlt utasításból ered, amely a Xerox Alto egyik nagyon hatékony blokkmozgató utasítása. Ez az egyik első olyan gép volt, mely támogatta a Smalltalkot. Mivel a nyelvben a képek kétdimenziós tömbök (a színes képernyők megjelenéséig bittömbök), így a leghatékonyabb módja a képek manipulálásának a résztömbökön, blokkokon végzett műveletek. A Smalltalk minden grafikus művelete leírható bitblokkok mozgatásával, vagyis egy forrásformból egy célformba való mozgatással (pl. képernyőre kirajzoláskor a célform maga a képernyő), melyeket a BitBlt objektumok írnak le.
A BitBlt objektumok a következőket tartalmazzák:
Forrásform (source form): a form, melyet, vagy melynek egy részét másoljuk.
Célform (destination form): a form, vagy egy része, melyet módosítanunk kell a másolás során.
Bitmaszk (halftone form): egy 16x16-os kép, melyet a másolás során maszkolásra, vagy mintával való kitöltésre használhatunk. A halftone form tehát egy 16x16-os bitkép, mely ismétlődve bemásolódik a célformba. Ha forrásformot is megadunk, a kettő és művelettel kapcsolódik össze, így lehetőségünk van a forráskép keverésére a textúrával (ld. az alábbi ábrát). A Form osztályszintű konstansokat (unáris műveleteket) is definiál, melyekkel könnyen létrehozhatunk ilyen bitképeket (az újabb implementációkból ez már valószínűleg hiányzik).
Kombinálási szabály (combination rule): azt írja le, hogy az eredmény milyen módon álljon elő a forrásform, a maszk pixeleiből, valamint a célform régi pixeleiből. Az értéke egy egész szám (1 és 16 között, az összes lehetséges módnak megfelelően), mely a mód sorszámát jelenti. Könnyítésképpen a Form osztály osztályszintű konstansokat definiál, pl. Form over hatására a célform felülíródik a forrással.
A forrás origója és mérete (source origin, extent): a forrás origója és a méret együtt meghatározzák a forrás formnak azt a részét, amelyből a másolás történik.
A cél origója (destination origin): az extent-tel együtt meghatározza a célformnak azt a részét, ahová történik a másolás.
Vágótéglalap (clipping rectangle): a forrásform meghatározása mellett megadhatunk egy téglalapot, a célformban az ezen kívül eső pixelértékek nem módosulnak.
BitBlt objektum létrehozásakor a fenti paraméterek mindegyikét meg kell adnunk (az egyes implementációk ettől eltérhetnek), az üzenet formája #destForm: #sourceForm: #halftoneForm: #combinationRule: #destOrigin: #sourceOrigin: #extent: #clipRect: . A megadott értékek utólag módosíthatók a különféle metódusok segítségével.
A BitBlt által leírt műveletet a #copyBits üzenet végzi el. A #drawFrom: startPoint #to: endPoint üzenettel egy vonal mentén is rajzolhatunk, melynek végpontjait a paraméterek adják. A közbülső pontokat a metódus a Bresenham algoritmussal határozza meg, és minden pontra meghívja a copyBits műveletet. Ennek a műveletnek a segítségével könnyen tudunk például különféle vonalstílusokat rajzolni.
A DisplayObject absztrakt osztály a kirajzolható osztályok közös interfésze. Ennek megfelelően a következő szolgáltatásokat biztosítja:
Kirajzolás egy DisplayMedium objektumra transzformációval, vagy anélkül. A DisplayMedium osztály írja le azt a protokollt, amely azokat az objektumokat jellemzi, amikre a Smalltalkban rajzolni lehet. Tehát DisplayObject típusú objektumok csak valamely display medium-ra rajzolhatók. Minden display medium maga is display object, így display medium is kirajzolható egy másik display mediumra.
Befoglaló doboz kiszámolása.
Átskálázás és tükrözés, valamint offset megadása. Érdemes megjegyezni, hogy ezeket a leszármazott osztályok különbözőképpen implementálhatják, egy objektum transzformálásakor bizonyos esetekben új objektum jön létre (ilyenek pl. a Path és leszármazottainak objektumai), míg más esetekben (pl. a Form esetében) az üzenetet fogadó objektum módosul.
Rajzoló műveletek:
#displayOn: aDisplayMedium #at:
aDisplayPoint #clippingRectangle: clipRectangle #rule: rulelnteger
#mask: aForm – ez a művelet a paraméterként megadott
display mediumra, a megadott pontba kirajzolja az üzenet
fogadóját. A további paraméterek megegyeznek a BitBlt
objektumoknál látottakkal, ezek írják le a blokk másolásának
pontos módját. Az egyes paramétereknek implementációtól
függően lehetnek alapértelmezett értékeik (vagyis Smalltalk
terminológiában az osztály objektumai rendelkeznek egy másik
üzenettel is, mely abban különbözik a fentitől, hogy néhány
paraméter nincs jelen, implementációja pedig a fenti üzenet
valamilyen paraméterértékekkel való hívásával történik). A
talán leggyakrabban használt ilyen üzenet a #display,
vagy a #displayAt: , melyek a képernyő bal felső sarkába,
illetve egy adott pontjára rajzolják az üzenet fogadóját. A
kirajzolásnak ezenkívül különböző, transzformációkkal
vegyített változatai is vannak, melyek leírják, hogy a
kirajzolt kép az eredetihez képest hogyan módosuljon (pl.
eltolás, átskálázás stb.).
A display object befoglaló téglalapjának kiszámítására a #computeBoundingBox üzenet szolgál, a már kiszámolt téglalapot a #boundingBox üzenettel kérdezhetjük le. Hasonlóan, a téglalapnak különböző adatai is lekérdezhetőek: a mérete, szélessége és magassága, melyeket rendre az #extent, #width és a #height adnak vissza.
Minden display object rendelkezik egy offsettel, mely azt mondja meg, hogy kirajzoláskor a rajzoló műveletben megadott koordinátához képest hol legyen a kép bal felső sarka. Ez például kurzorok esetén hasznos, mikor a képernyő egy adott pontján szeretnénk megjeleníteni a kurzort oly módon, hogy a képpontra ne a kurzor képének bal felső sarka essen, hanem az ábrázolt kép egy másik pontja, például a nyíl hegye. Ilyenkor nincs más dolgunk, mint beállítani az offsetet a megfelelő értékre, és ezt követően minden kirajzoláskor a célpixelre a kurzor képének bal felső sarka helyett a kívánt pontja kerül. Az offsetet explicit módon is megadhatjuk az #offset: üzenettel, valamint különböző műveletek segítségével módosíthatjuk, például átskálázhatjuk, vagy eltolhatjuk egy adott értékel.
A DisplayObject osztály még egy érdekes művelettel rendelkezik, mely egyszerű animációk készítését hivatott szolgálni. A #follow: locationBlock #while: durationBlock üzenet hatására a kép a locationBlock által leírt pozícióban fog újra meg újra kirajzolódni egészen addig, amíg a durationBlock igaz értéket ad vissza. Továbbá, leginkább ablakok és kurzor mozgatás után történő rajzolásának megkönnyítésére két műveletet biztosít, melyekkel könnyedén visszaállítható a korábbi háttér. A #backgoundAt: location üzenettel lekérdezhetjük a location helyen található hátteret, míg a #moveTo: newLocation #restoring: backgroundForm üzenettel pedig a kép elmozdítása után visszaállítható a korábbi háttér.
Az osztálynak azon kívül, hogy a DisplayObject típusú objektumok csak az ilyen típusú objektumokra rajzolhatnak, más szerepe is van.
Az egyik ilyen, hogy műveleteket biztosít a kép kitöltésére, például a #black üzenet hatására a kép minden pixele fekete lesz. Hasonlóan színezhető a kép egy része, ekkor egy téglalap paramétert is meg kell adnunk, amely leírja a kitölteni kívánt részt. Lehetőségünk van még adott mintával (textúrával) való kitöltésre is, ezt implementációtól függően valamely #fill: kezdetű üzenettel tehetjük meg.
Az osztály másik fontos szolgáltatása, hogy a kép köré keretet rajzolhatunk (ami jelen esetben nem tényleges rajzolást, hanem a kép reprezentálására használt kétdimenziós pixeltömb módosítását jelenti). Szintén implementációtól függően, a #border: kezdetű üzenetekkel rajzolhatunk keretet, különböző vastagsággal, színnel, vagy mintával, illetve lehetőség van nem egyforma szegélyű keret rajzolására is.
Az osztály ezenkívül definiál két alacsonyszintű műveletet is, melyek a BitBlt copyBits és drawLine műveletével azonos módon a kép két része között másol, illetve rajzol.
A Smalltalkban a Form osztály reprezentálja a grafikus képeket. A DisplayMedium, és rajta keresztül a DisplayObject leszármazottja, így rendelkezik az ott ismertetett szolgáltatásokkal.
A Form osztály a reprezentált kép leírására adattagokat is bevezet: a height és a width tartalmazza a kép magasságát és szélességét pixelben mérve, a bits tömb pedig a kép pixeleit írja le. Mivel az osztály a DisplayObject leszármazottja, így a reprezentált képek offsettel is rendelkeznek.
A pixelek leírására használt kétdimenziós bits tömb típusa és megvalósítása implementációfüggő. Minden implementáció lehetőséget ad a tömb elemeinek közvetlen elérésére és manipulálására, melyet a Form objektumok műveletein keresztül közvetve is megtehetünk, azaz a Form is biztosít olyan üzeneteket, melyek a bits tömb egyes elemeit külön-külön érik el.
Formok létrehozása kétféleképpen történhet. Az egyik lehetőség, hogy a téglalapoknál látott módon és szintaktikával a felhasználó jelöli ki a képernyőn a kép határait, a bits tömbbe pedig a képernyő megfelelő részének tartalma kerül. A másik lehetőség, hogy a bits tömböt explicit megadjuk. Ez utóbbit segítendő néhány hasznos műveletet is definiál az osztály. Például a #dotOfSize: diameter üzenettel egy új Form jön létre, mely egy diameter átmérőjű kört ábrázoló képet reprezentál. Mindkét, előbb felsorolt módszert már meglévő objektumokra is alkalmazhatjuk, a reprezentált kép lecserélésére.
A képek különféle transzformációira is lehetőségünk van. Az alább felsorolt műveletek mind új formot adnak vissza. A #magnifyBy: scale és a #shrinkBy: scale a kép nagyítására, illetve kicsinyítésére szolgál, míg forgatni a #rotateBy: angleSpecification üzenettel tudunk. A forgatás csak 90 fok többszöröseivel megengedett, a paraméter írja le, hogy hányszor 90 fokkal forgatunk. A #reflect: specificationPoint szolgál a tükrözésre, a paraméter megfelelő koordinátája 0, ha az adott tengely mentén nincs tükrözés, 1 egyébként. A fentieken kívül még kitöltés művelet is van, mely a megadott maszkkal kitölt egy zárt alakzatot, melynek pontja a megadott paraméter. Alakja: #shapeFill: aMask #interiorPoint: interiorPoint.
Az InfiniteForm tulajdonképpen egy végtelen kiterjedésű kép, és leginkább háttér rajzolására használjuk. Új objektumot a #with: üzenettel hozhatunk létre, paramétere a minta, amely a végtelenségig ismétlődik. Fontos megjegyezni, hogy az osztály a neve ellenére nem a Form leszármazottja, hanem közvetlenül a DisplayObject gyereke, így csak az alapvető rajzoló funkciókat örökli (de ez éppen elég, például végtelen kiterjedésű kép esetén nincs értelme szegélyt rajzolni). Viszont az ismétlődő minta Form objektum, tehát erre minden alkalmazható, amit az imént láttunk.
A ColorForm a Form osztály leszármazottja, mely színes képek kezelését is lehetővé teszi.
A grafikus kurzorok a Smalltalkban a Cursor osztály példányai. Az osztály a Form leszármazottja, így rendelkezik annak minden szolgáltatásával. Ezen felül, mivel a kurzorok a felhasználó irányába történő visszajelzés fontos eszközei, így ennek támogatására további műveleteket is bevezet.
A Smalltalk Cursor minden esetben 16x16 pixel méretű képet jelképez. Rajzoláskor az offset értékének itt különösen fontos szerepe van, az offset segítségével érhető el, hogy a kurzor megfelelő pontja essen a rajzolási pontra.
Kurzorok létrehozása egyrészt a Formnál látott módon, a kép és a további adattagok explicit megadásával történhet, emellett az osztály több osztályszintű konstanst (unáris üzenetet) is definiál, melyek a különböző kurzorok létrehozását teszik lehetővé. Az alábbi ábrán ezek közül néhány látható.
Egy időben csak egyetlen kurzor lehet aktív. Aktív kurzor az, amelyet az egérrel irányíthatunk. Ennek vezérlésére az InputSensor osztály használatos, mely a beviteli eszközök kezelésére létrehozott interfész (a Smalltalk billentyűzet és háromgombos egér meglétét feltételezi). Az InputSensor osztály közvetlenül az Object leszármazottja. A beviteli eszközökhöz való hozzáférés a Sensor globális változón keresztül történik, mely az osztály egyik példányára mutat. Az aktuális kurzor lekérdezésére és beállítására a #currentCursor és a #currentCursor: newCursor üzenetek szolgálnak. A #cursorPoint és a #cursorPoint: aPoint szolgál a kurzor által mutatott pozíció lekérdezésére és beállítására. A felhasználóval való interakciót támogató műveletek között többek között olyanok találhatók, melyek lekérdezik, hogy történt-e egérgomb lenyomás (pl. #anyButtonPressed), vagy amelyek egy adott interakcióra várnak (pl. #waitClickButton egérkattintásra vár, majd visszaadja a kattintás helyét). Az aktív kurzort nem csak az InputSensor, hanem a Cursor műveletein keresztül is beállíthatjuk: a #show művelet a kurzort aktívvá teszi, a #showGridded: gridPoint aktívvá teszi és mindig a megadott rácsra illesztve jeleníti meg, míg a #showWhile: aBlock a blokk terminálásáig teszi aktívvá a fogadó objektum által reprezentált kurzort.
A képernyők reprezentálására a DisplayScreen osztály szolgál, mely a Form leszármazottja. A képernyőn aktuálisan látható képet a Display globális változó által mutatott példány reprezentálja. A DisplayScreen osztálynak lehet több példánya is, ami például animációnál lehet hasznos, a dupla pufferelés nagyon könnyen megvalósítható a segítségével (kirajzoljuk a Displayt, majd egy másik DisplayScreen objektumba kirajzoljuk a következő képkockát, végül megcseréljük a két képet).
Az osztály műveletei között szerepel az aktuális képernyő beállítása, melyre a #currentDisplay: aDisplayScreen szolgál. A #displayExtent: aPoint üzenet küldésével beállíthatjuk az objektum képméretét (amely egy logikai méret, vagyis eltérhet a képernyő fizikai méretétől). Valamint érdemes még megemlíteni a #flash: aRectangle üzenetet, amely felvillantja a képernyőnek a paraméterben leírt téglalap méretű részét (ezzel pl. kurzorvillogást könnyű implementálni).
A Pen egy speciális BitBlt, vagyis rajzolási műveleteket reprezentál. A toll egy olyan rajzeszköz, amelyet bárhová elhelyezhetünk a képernyőn, áthúzhatjuk máshová, vagy adott irányba eltolhatjuk. A mozgások közben, ha a toll nincs felemelve, a mozgás mentén vonalat húz maga után. Ennek megfelelően az osztály a BitBlt attribútumai mellé még a következőket veszi fel:
pen state: a toll állapota, azaz felemelt vagy leengedett. A troll csak akkor rajzol, ha le van eresztve.
location: a toll helye a képernyőn.
direction: az az irány, amely felé indulni fog, ha az elmozdulás műveleteket hívjuk. A 0 fok jelenti az északi irányt, és az óramutató járásával megegyező irány a pozitív.
drawing frame: szerepe a BitBlt clipping rectangle attribútumával azonos.
A Pen rendelkezik a paraméter nélküli #new konstruktorral is. Az így létrehozott toll le van eresztve, a teljes képernyőre rajzol, és észak felé néz. Az osztály a különböző attribútumok állítására természetesen rendelkezik a megfelelő metódusokkal, vagyis a toll felemelése (#up) és leeresztése (#down), mozgatása (#goto: aPoint), elhelyezése (#place: aPoint). Elhelyezés esetén semmiképp sem húz vonalat. Ezenkívül a Smalltalk a Pennel átvette a LOGO műveleteit is, vagyis a különböző irányú elfordulásokat és mozgásokat.
Itt található egy példaprogram, mely egy nagyító funkcióval ellátott kurzor elkészítésén keresztül mutatja be a fenti grafikus osztályokat és alkalmazásukat.
A Morphic-ot a Smalltalk-80 imént bemutatott Model View Controller grafikus felületét hivatott leváltani. A Squeak környezetben GUI-programozáshoz a Morphic már alapértelmezetten rendelkezésre áll, a 3.8-as verziótól kezdve pedig az MVC-t ki is vették belőle. Eredetileg a Self programozási nyelvhez készült, innen lett átemelve a Squeak-be.
A Morphic-ban minden, a grafikus felülethez tartozó objektum a Morph osztályból származik. (Az osztály neve a görög “alak, dolog” szóból ered.) Mint említettem, a Squeak-ben a Morphic az alapértelmezett GUI-programozási módszer – olyannyira, hogy a felület minden megjelenített eleme (ablakok, menük stb.) egy-egy morph. Sőt, maga az egész Squeak ablak is egy, a Morph-ból származtatott osztály: a World. De mindez most csak azért érdekes, hogy látni lehessen, a Morphic egy mindent átfogó, uniform módszer.
Egy morph-hoz hozzá lehet adni al-morph-okat (submorph). Egy submorph ugyanúgy teljes értékű morph, tehát neki is lehetnek al-morph-jai. Egy morph-nak akárhány submorph-ja lehet. Ha egy morph-ot mozgatunk, a submorph-jai is ugyanúgy vele mozognak, de kódból lehet külön egy submorph-ot is mozgatni. Egy morph-hoz bármikor adhatunk és el is vehetünk submorph-ot.
Hogy jobban megértsük, mit is jelent a submorph, vegyünk egy egyszerű példát a TicTacToe példaprogramból (a teljes programot ld. Lentebb). Egy játék végén felugrik egy ablak, amely kiírja, hogy az egyik játékos győzött. Ez az ablak a következőképpen néz ki:
Maga az ablak természetesen egy Morph, amelyen a kiírt szöveg egy submorph, az újraindításhoz használt gomb szintén. Az újraindítás gombon a “Restart” felirat pedig a gombnak egy submorph-ja.
Az osztály fontosabb attribútumai:
owner: A morph szülője, tehát az a morph amelynek ő egy al-morph-ja, ha létezik ilyen.
submorphs: Egy tömb, ami az al-morph-okat tartalmazza.
bounds: Egy “Rectangle” osztálybeli objektum. A morph pozícióját és méreteit kérdezhetjük le és állíthatjuk be a segítségével.
color: A morph színe.
Egy morph-ot az openInWorld metódus segítségével jeleníthetünk meg. A neve alapján ez az eljárás a “World”-ben, tehát a Squeak ablakban megnyitja a definiált morph-ot.
Bármely Morph képes eseménykezelésre. A kezelhető események többek között az egérműveletek (kattintás, dupla kattintás, gomb lenyomása, a kurzornak az objektum fölé mozgatása... stb.), “drag n drop” (vonszolás), valamely billentyű lenyomása.
Vegyük példaképp a “mouseDown”, azaz egy egérgomb lenyomása akciót. Először is, ha azt szeretnénk, hogy a morph-unk kezelje ezt az eseményt, felül kell definiálni a handlesMouseDown: anEvent műveletet. Ez a művelet mindössze visszaad egy igaz vagy hamis értéket attól függően, hogy az adott eseményhez létezik-e eseménykezelő. Alapértelmezettként ez a függvény hamis értéket ad vissza. Ha felüldefiniáljuk úgy, hogy igazzal térjen vissza, az akció megtörténtekor automatikusan meg fog hívódni a mouseDown: anEvent művelet, amelyet nyilván szintén a mi dolgunk felüldefiniálni – ez lesz az eseménykezelő. Az anEvent paraméter egy MouseEvent típusú objektum. Az eseménnyel kapcsolatos mindenféle hasznos információt tartalmaz: többek között a kurzor pozíciójának koordinátáit, illetve azt, hogy melyik gomb lett lenyomva.
Az események osztályai:
MorphicEvent: Minden esemény osztály őse. Különböző metódusokkal le lehet kérdezni az objektumtól, hogy a MorphicEvent mely alosztályához tartozik a példányosított objektum (pl. a billentyűzethez, vagy az egérhez tartozik-e, stb.). Ezek az altípust lekérdező műveletek az összes esemény típusra jellemzőek.
stamp: Időbélyeg, amikor az esemény történt.
source: Az eseményt generáló objektum.
UserInputEvent: Az olyan eseményeket reprezentálja, amelyeket a felhasználó idézett elő.
position: Egér kurzor pozíciója.
handler: Az eseményt kezelő objektum.
wasHandled: Logikai változó, amely azt mondja meg, hogy le lett-e kezelve az adott esemény.
KeyboardEvent: A billentyűzet valamely billentyűjének lenyomása.
keyValue: Értéke a lenyomott gombot határozza meg.
MouseEvent: Az egér valamely gombjának lenyomása, vagy az egérkurzor valamilyen objektum fölé kerülése. A UserInputEvent osztályban már megtalálható az összes szükséges attribútum.
A Smalltalk-fejlesztők jónéhány, gyakran használt osztályt definiáltak a Morphic segítségével, amelyek a Squeak-ben megtalálhatóak.
StringMorph: Más programozási nyelvekből talán ismerős a “label” grafikai elem. A StringMorph lényege gyakorlatilag ugyanaz, mint a label-nek: egy, a felhasználó számára csak olvasható string-et ábrázol.
contents: Az ábrázolt szöveg.
font: Az ábrázolt szöveg betűtípusa, betűmérete.
emphasis: Olyan tulajdonságokat lehet a segítségével megadni, mint pl. aláhúzott, félkövér, dőlt betűk.
TextMorph: A StringMorphnál kicsit többet tud, például az ábrázolt szöveg egyes részeinek különböző stílusa lehet, a felhasználó változtathatja a tartalmat, tartalmazhat linkeket, illetve tárolja a tartalomban véghezvitt változtatásokat.
ImageMorph: Ezen morph segítségével egy külső file-ból betöltött képet ábrázolhatunk.
image: Az ábrázolt kép.
Néhány, az ablakok kezeléséhez konkrétan kapcsolódó példa:
SystemWindow: Egy ablak a megszokott “Kis méret”, “Teljes méret”, “Bezárás” gombokkal ellátva.
SimpleButtonMorph: Gomb osztály, amely abban különbözik egy “sima” morph-tól, hogy rendelkezik felirattal, amely az osztály egy attribútuma.
initializeWithLabel: labelString: Ha ezen metódus segítségével hozzuk létre a gombunkat, akkor megadhatjuk a létrehozáskor a feliratot, nem kell külön utána beállítani.
A setDefaultLabel: label eljárással meg lehet adni alapértelmezett feliratot, hogy ne kelljen külön ezt megadni, ha több ugyanolyan feliratú gombot szeretnénk (ötlet pl. “OK” gomb osztály, “Mégse” gomb osztály).
ProgressBarMorph: Segítségével egy hosszabb folyamat végrehajtása esetén a haladást tudjuk jelezni – egy csík töltöttsége jelzi, hogy hol tart a folyamat.
progressColor: A progressBar színét jelző attribútum.
value: Az “érték” attribútum, amely a folyamat teljesítettségét, tehát a progressBar töltöttségi szintjét jelzi.
update: A value érték változásának jelzése – megváltoztatja a csík töltöttségét is.