[Monkey] GUI Tutorial
Übersicht Andere Programmiersprachen FAQs und Tutorials
MidimasterBetreff: GUI Tutorial |
Fr, Aug 03, 2012 10:27 Antworten mit Zitat |
|
---|---|---|
Dieses Tutorial wird darstellen, wie man eine GUI in Monkey erstellt. Es geht nicht so sehr um den optischen Teil, sondern mehr um die Logik, wie man mit Klassen und Listen an eine solche Frage herangeht. Außerdem wird die GUI auflösungsunabhängig für die verschiedenen Display sein.
Hier mal ein erster Blick drauf. Wie man sieht geht es in Richtung Android GUI: Theoretisch lässt sich das Wissen auch auf Bmax übertragen. Bis auf wenige Besonderheiten wird der Code auf beiden Targets ausführbar bleiben. Dies ist kein Anfänger Tutorial. Es geht nicht um die Verwendung von GUIs, sondern um die Programmierung einer neuen GUI. Wie bei allen Midimaster-Tutorials werde ich die Kapitel jeweils in eigene Postings stecken und bitte deshalb die Admins, mir ein Mehrfach-Posting zu erlauben. Ebenso soll die Abfolge der Kapitel nicht durch Fragen und Kommentare gestört werden, daher bitte ich alle Antworten hier zu posten: https://www.blitzforum.de/foru...hp?t=33457 . Fragen von Hilfesuchenden bitte hierhin https://www.blitzforum.de/forum/viewforum.php?f=50 und auf keinen Fall in diesen Thread. Achtung: Das Tutorial wird derzeit überarbeitet. Bisher habe ich nur Kapitel 1 bis 3 aktualisiert. Die Kapitel 4 bis 6 passen deshalb nicht mehr richtig dazu. Geplante Fertigstellung Ende Juli 2013 Die Kapitel: Kapitel 1: Alles Gadget oder was? Kapitel 2: Mütter und Kinder Kapitel 3: Touch Down Kapitel 4: Etwas Styling Kapitel 5: Scrolling Kapitel 6: Texteingabe |
||
- Zuletzt bearbeitet von Midimaster am Do, Jul 04, 2013 0:46, insgesamt 14-mal bearbeitet
MidimasterBetreff: Kapitel 1: Alles Gadget, oder was? |
Fr, Aug 03, 2012 12:05 Antworten mit Zitat |
|
---|---|---|
Kapitel 1: Alles Gadget, oder was?
Zunächst der übliche Monkey-Überbau: BlitzMax: [AUSKLAPPEN] Strict So fang ich üblicherweise immer Programme an. Es gibt schon die Klasse Gadget, die später die GUI darstellt, es gibt dort auch schon Funktionen, die vom Hauptprogramm MyGame aufgerufen werden, aber die Funktionen sind kompett inhaltslos. Der Vorteil eines solchen Aufbaus ist, dass das Programm schon läuft! Späteres Hinzufügen von Code kann also immer gleich getestet werden: "Wenn das Programm nicht anläuft muss es an dem neuen Code liegen.". Die Elemente der GUI selbst werden aus immer dem gleichen Typen bestehen. Wir nennen es Gadget. Es kann Frame und Label sein, Button und Ceckbox, Scrollfenster und Silder und, und und.... Jedes dieser Elemente hat eine eindeutige Variable Typ. Daran unterscheidet dann die Arbeitweise des Gadget. Außerdem die Üblichen Werte wie X, Y, Breite, Höhe, ... . Ein Element kennt immer seine Mutter, seine Kinder tragen sich später in die Children-Liste. So kennt es später also auch seine Kinder. Daraus ergeben sich die ersten Fields, die unser Gadget haben muss: BlitzMax: [AUSKLAPPEN] Strict Wie ihr seht bleibt alles noch sehr abstrakt und völlig ohne Malen. Dies werde ich erst in Kapitel 2 zeigen. Unsere GUI benötigt ein "Mutter"-Fenster. Weitere Gadgets sind Kinder oder Enkel dieser Mutter . |
||
- Zuletzt bearbeitet von Midimaster am So, Jun 16, 2013 15:33, insgesamt 4-mal bearbeitet
MidimasterBetreff: Kapitel 2: Mütter und Kinder |
Sa, Aug 04, 2012 1:45 Antworten mit Zitat |
|
---|---|---|
Kapitel 2: Mütter und Kinder
Eine der zentralen Eigenschaften der GUI ist es, zu wissen wo und ob etwas hingemalt werden muss und was der User gerade auswählt. Hier hilft das Mutter-Kinder-Prinzip, das jedes Gadget genau in einen Container unterbringt. Der Container wiederum kann viele Elemente enthalten. Diese Elemente können selbst wieder Container für weitere Gadgets sein. Dies entspricht dem Vorbild einer "Mutter", die viele Kinder haben kann, die wiederum Kinder haben können. Aber jedes Kind hat immer nur eine Mutter. So kann man sich z.b. ein Scroll-Fenster als rechteckige Mutter vorstellen, die zwei Kinder nebeneinander hat: den rechteckigen sichtbaren Image-Bereich und ein rechteckiges Scroll-Element.Der sichtbare Image-Bereich ist wiederum Mutter des Bildes, das viel größer als der sichtbare Bereich sein kann. Das Scroll-Element ist wiederum Mutter für den rechteckigen Slider, der sich darin bewegen kann. So lassen sich auch scheinbar komplexe Gadget letzendlich in viele kleine nebeneinander oder ineinanderliegende Rechtecke darstellen. Sehen wir uns zunächst an, wie man die Gadgets zeichnet: Es beginnt immer bei der Mutter aller Mütter. Dem einzigen Gadget, dass selbst keine Mutter hat. Oft wird es "Fenster" genannt. BlitzMax: [AUSKLAPPEN] Function Draw%() Die Funktion Gadget.Draw() startet eigentlich nur das Malen der ÜberMutter, die dann rekursiv auch ihre Töchter malen wird. Die Parameter die dabei übergeben werden sind die Koordinaten und Malgrenzen des Bildschirms. Der Scissor() ist eine Funktion von Monkey, die ähnlich dem SetViewPort() aus BMax den Malbereich auf ein Rechteckauschnitt beschränkt. Zunächst steht er für die Übermutter auf den Grenzen des Bildschirms. Später werden die Gadgets ihre eigenen Grenzen so an die Kinder weiterreichen. Das Malen erfolgt in der Methode Gadget.DrawOne() BlitzMax: [AUSKLAPPEN] Method DrawOne%(offX%,offY%,sX%,sY%,sX2%,sY2%) Ist das Gadget nicht visible wird gleich abgebrochen und so auch die Kinder erst gar nicht gemalt. Danach werden zu den eigenen Koordinaten X Y die Koordinaten der Mutter offX offY addiert. Nun wird der Scissor auf seine zukünftigen Grenzen eigestellt. Sind die Ausmaße des Gadget enger als der bisherige Scissor, werden diese Aumaße als neue Grenzen des Scissors festgesetzt: BlitzMax: [AUSKLAPPEN] If offX>sX Then sX=offX Die letzt Zeile erzwingt einen Abbruch des Malens, wenn der Scissor jetzt eine Breite oder Höhe unter Null haben sollte. Dies wird immer dann der Fall sein, wenn das Gadget außerhalb des sichtbaren Bereichs liegt. Auch die Kinder werden so nicht mehr gemalt. Schließlich wird gemalt. Während der Erprobungsphase genügt es hier ein farbiges Rechteck zu zeichnen. Wenn des damit funktioniert wird es später auch mit aufwendigen Themes gelingen. Nun kommt der interessanteste Teil. Das Gadget ruft seine Kinder auf und übergibt als Parameter seine eigenem X Y Koordinaten, sowie den soeben neu ausgerechneten Scissor: BlitzMax: [AUSKLAPPEN] For Local loc:Gadget = EachIn Children Nachzutragen bleibt nur noch eine kleine Funktion SetFarbe(), die während der Simulation zur Unterscheidung der Gadgets dient. Sie legt für die Gadgets unterschiedliche Farben fest. Später könnte man hier komplette Themes und Styles festlegen. Und so sieht nun unsere GUI momentan aus: BlitzMax: [AUSKLAPPEN] Strict In der Praxis Hier einige Experimente, die Ihr jetzt schon mit diesem Teil ausprobieren könnt. Dazu müsst ihr im Code siehe oben immer nur die Class MyGame Extends App durch die Beispiele ersetzen. Mutter und Tochter wenn die Mutter bewegt wird geht die Tochter mit: BlitzMax: [AUSKLAPPEN] Class MyGame Extends App Fenstergrenzen Als Alternative bewegt Ihr mal die Tochter. Tatsächlich sorgt die Gadget.Draw() bereits dafür, dass die Tochter nicht über die Mutter hinausgemalt wird: BlitzMax: [AUSKLAPPEN] Method OnUpdate%() unterschiedliche Gadgets Oder ändert mal den "Typ". Sofort ändert sich die Farbe des Gadget: BlitzMax: [AUSKLAPPEN] ' die "0" ist der Typ: Z-Order: Wer überdeckt wen? Zwei Gadgets auf der selben Ebene. Das zuerst erstellt wird zu unterst gemalt: BlitzMax: [AUSKLAPPEN] Method OnCreate%() Verschachtelung Mutter, Tochter, Enkel: BlitzMax: [AUSKLAPPEN] Method OnCreate%() Ein Slider entsteht aus 4 Grundgadgets: BlitzMax: [AUSKLAPPEN] Class MyGame Extends App |
||
- Zuletzt bearbeitet von Midimaster am Mi, Jun 19, 2013 11:11, insgesamt 9-mal bearbeitet
MidimasterBetreff: Kapitel 3: Touch |
Sa, Aug 04, 2012 9:57 Antworten mit Zitat |
|
---|---|---|
Kapitel 3: Touch
In diesem Kapitel werten wir die Aktionen aus, die in der GUI geschehen können. Hierfür brauchen wir weitere globale Parameter und Konstanten: BlitzMax: [AUSKLAPPEN] Class Gadget Im globalen Gadget EventSource merken wir uns das Gadget, das die Maus beim Downklicken getroffen hat. In den Variablen StartMausX und StartMausY merken wir uns die Koordinate, die die Maus in diesem Moment hatte. Das Checken der Maus startet in Gadget.Check() und beginnt mit dem Check der Übermutter, die dann rekursiv alle ihre Kinder in der Gadget.CheckOne() rufen wird. Zunächst sieht die Funktion Gadget.CheckOne() der DrawOne()-Funktionen sehr ähnlich: BlitzMax: [AUSKLAPPEN] Method CheckOne:Gadget(offX%,offY%,sX%,sY%,sX2%,sY2%) Die Gadget.CheckOne() erhält beim Aufruf wieder die Koordinaten der Mutter sowie den von ihr gesetzten Scissor. Die Tests und Berechnungen sind die gleichen wie bei Draw. Danach wird untersucht, ob die Maus sich innerhalb des Gadget befindet: BlitzMax: [AUSKLAPPEN] .... Nur wenn dies der Fall ist, werden auch die Kindern durchsucht. Wäre die Maus auch dort, würde dies vorgehen. Rekursionen sind immer schwer in ihrer Gesamtwirkung zu verstehen. Doch es gilt: "Funktioniert es für ein Element, dann funktioniert es auch für alle anderen, dann funktioniert es auch für das Ganze." In der Hauptfunktion Gadget.Check() ändert sich mehr: Dort wird der Zustand der Maus geprüft und dann unterschieden, ob sie gedrückt, gehalten oder losgelassen wird: BlitzMax: [AUSKLAPPEN] Function Check:Gadget() Ist die Maus gedrückt und war vorher noch kein Gadget als gedrückt gemeldet worden, wird untersucht welches Gadget die Maus anklickt. Ist die Maus gedrückt und vorher war schon ein Gadget als gedrückt gemeldet worden, handelt es sich wahrscheinlich um ein Scrollen oder ziehen. War die Maus gedrückt und wurde jetzt losgelassen dann wird ein Event gefeuert: "EVENT_KLICK" Die Funktion gibt als Rückgabeparameter das Gedgat an, in dem die Action stattgefunden hat. So können wir später im Hauptprogramm darauf zugreifen. Die Touch-Ereignisse verarbeiten wir in einer eigene Funktion, die in der Lage ist, ziehende Gesten von einfachen Punktberührungen zu unterscheiden: BlitzMax: [AUSKLAPPEN] Function MausAction:Void() Die Funktion stellt die Werte MausX% MausY% und MausState% zur Verfügung. MausState% kennt dabei drei Zustände: 0=Touch nicht gedrückt 1=Touch ist gedrückt 2=Touch wurde losgelassen Beim Maus-Klicken wechselt MausState zunächst auf 1 und springt beim Loslassen dann in den Zustand 2. Damit lässt sich "Ziehen" von "Klicken" unterscheiden. Abschließend wieder der gesamte Code: BlitzMax: [AUSKLAPPEN] Strict |
||
- Zuletzt bearbeitet von Midimaster am Do, Jul 04, 2013 0:43, insgesamt 7-mal bearbeitet
MidimasterBetreff: Kapitel 4: Etwas Styling |
Sa, Aug 04, 2012 10:44 Antworten mit Zitat |
|
---|---|---|
Kapitel 4: Etwas Styling
In diesem Kapitel wollen wir ordentlich Styling in die GUI bringen: - Die Gadgets erhalten Rahmen und Innenbereich - Die Gadgets erhalten Text mit dem AngelFont() Modul - Die GUI soll auflösungsunabhängig für alle Smartphones werden. Rahmen und Innenbereich Grundlage jedes Gadget wird ab jetzt ein sehr kleines PNG-Bild. Es enhält neun Bereiche aus denen die GUI dann die 4 Ecken, 4 Seiten-Rahmen und das Zentrum des Gadget zusammensetzt. Code: [AUSKLAPPEN] ------------------------ |Ecke Rand Ecke| | | |R R| |a Mitte a| |n n| |d d| | | |Ecke Rand Ecke| ------------------------ Eine eigene Klasse GUIElement wird dafür zuständig sein. GUIElement läd die Vorlagen-PNGs und rendert daraus durch zusammensetzen von 9 Elementen ein Image, dass es dann an die eigentliche GUI zurückgibt. Die stellt ab jetzt das Gadget nicht mehr durch ein DrawRect() dar, sondern jetzt mit DrawImage() Die Vorlagen-PNGs müssen so beschaffen sein: Größe mindestens 10x10 Pixel. Daraus entsteht ein Bild ohne Rahmen. Hat die Vorlage 12x12 Pixel so wird das GUI-Element einen Rahmen von 1 Pixel Breite haben (1+10+1=12). Hat die Vorlage 16x16 Pixel so wird das GUI-Element einen Rahmen von 3 Pixel Breite haben (3+10+3=16), u.s.w. Für die Vorlagen kann man beliebige Namen vergeben. Sie werden von der GUI automatisch gefunden. Jede Vorlage steht für einen neuen Gadget-Typen. BlitzMax: [AUSKLAPPEN] Method Render:Image(B#,H#,Text$) Text im Gadget AngelFont ist ein Modul, dass mit Monkey kostenlos ausgeliefert wird. Es befindet sich im Unterverzeichnis "Project/monkey/bananas/beaker". AngelFont kann Zeichensätze korrekt auf Flächen rendern. Zur Einbindung sind nur wenige Schritte nötig: BlitzMax: [AUSKLAPPEN] Strict Nun muss noch ein Ordner MyGame.data angelegt werden, in den folgende drei Dateien aus dem "Project/monkey/bananas/beaker" kopiert werden müssen: Code: [AUSKLAPPEN] angel_verdana.fnt
angel_verdana.png angel_verdana.txt Außerdem müssen die drei Dateien angelfont.monkey, kernpair.monkey, und char.monkey in den selben Ordner kopiert werden, wo sich unser Hauptcode MyGame.monkey befindet Auflösungsunabhängige Darstellung Das auflösungsunabhängige Darstellen ist weitaus leichter, als man glauben könnte. Nur wenige Zeilen ändern alles: Wir führen zwei Variable XRatio# und YRation# ein, die zukünftig alle Zeichen aktionen dehnen. So können wir immer für die gleiche "virtuelle" Bildschirmgröße 320x480 programmieren und die Dehungsfaktoren werden dies korrekt auf das jeweilige Gerät rendern. BlitzMax: [AUSKLAPPEN] Class MyGame Extends App Bei den Touch()-Aktionen werden Sie die Werte stauchen und so genau das Gegnenteil bewirken. BlitzMax: [AUSKLAPPEN] Class Gadget Beim Zeichnen gibt es dank strenger Trennung des Codes nur zwei Stellen, wo die die Dehnung erwähnen müssen: BlitzMax: [AUSKLAPPEN] Class Gadget Die Monkey-Funktion Scale() ändert nicht nur Breite und Höhe aller Zeichenelementen. Sie beeinflusst auch die X und Y-Koordinaten. Einzig der Scissor muss noch umgerechnet werden. Das war's. Nachtrag zum Styling: schon im vorherigen Kapitel habe ich aus die eine DrawRect()-Zeile, die uns bisher gereicht hatte um ein Gadget vorläufig zu malen, durch ein schöneres Design mit Rahmen und Innenrechteck in zwei verschiedenn Farben ersetzt. Die Farben sind in der Funktion SetFarbe() notiert. sie beginnen mit der Nummer 100 für GadgetTyp 0, 110 für GadgetTyp 1, 120 für GadgetTyp 2, usw. Dadurch kann ich jetzt bis zu 10 Farben pro Gadget definieren: 100, 101, 102..bis 109 und so bis zu 10 grafische Elemente kombinieren um ein Gasget wirkungsvoller darzustellen. Abschließend noch der Code, wie er sich bs jetzt gesamt darstellt: BlitzMax: [AUSKLAPPEN] Strict in der nächste Lektion geht es um das Scrollen.... |
||
- Zuletzt bearbeitet von Midimaster am Do, Jul 04, 2013 1:26, insgesamt 5-mal bearbeitet
MidimasterBetreff: Kapitel 5: Scrolling |
So, Aug 05, 2012 9:47 Antworten mit Zitat |
|
---|---|---|
Kapitel 5: Scrolling
Heute geht es um das Bewegen eines Gadget in seiner Mutter. Dabei gibt es mehrere Parameter. Zunächst könnte das Bewegen verboten sein oder man kann horizontal oder vertikal scrollen oder beide Richtungen sind erlaubt. Außerdem sehe ich zwei grundsätzliche Arten der Bewegung Grenzen zu setzen: Das innere Objekt, darf so weit im äußeren Objekt bewegt werden, bis es an eine äußere Grenze stößt: IN_BORDER. (Beispiel:Billiardkugel auf dem Billiardtisch). Oder das Objekt ist größer als seine Mutter und darf nur solange bewegt werden, bis ein Rand sichtbar werden würde: OUT_BORDER. (Beispiel: großes Image im Fenster). Daher führen wir diese globalen Konstanten und einige Variable ein: BlitzBasic: [AUSKLAPPEN] Const MAUS_DOWN%=1,MAUS_HIT%=2,MAUS_OFF%=0 Nun könnte es immer sein, dass zwar ein Gadget nicht scrollen darf, aber seiner Mutter ist es erlaubt. z.B. eine Liste von Texten ist länger als das Fenster. Dann gibt es drei Gadgets-Ebenen: Das Fenster, der Listen-Container und viele Label-Gadgets, die sich dicht an dicht auf dem Container befinden. Nun soll sich mit einer Schiebe-Geste auf ein Label-Gadget nicht das Gadget gegen seine Brüder verschieben lassen, aber der darunterliegende Container im Fenster schon. Also müssen die Touch()-Aktionen auch an die darunterliegenden Mütter weitergereicht werden. BlitzMax: [AUSKLAPPEN] Method Scroll:Void() Dies bewirkt, dass die Scroll-Geste dann weitergereicht wird, wenn das Gadget selbst nicht scrollen darf. Darf das Gedget selbst scrollen, dann folgen zwei Routinen für vertikale und horizontale Bewegung. Hier die vertikale: BlitzMax: [AUSKLAPPEN] ... Else Bei der Erstberührung wurde die Position des Maus in StartMausY% vermerkt. Hat seitdem eine weitere Maus-Bewegung stattgefunden wird sie jetzt zur Gadget-Position addiert und StartMaus relativiert. Abschließend wird noch geprüft, ob die vorgegebenen Grenzen eingehalten werden. Bei OUT_BORDER darf der Rand des Gadgets nicht innerhalb seiner Mutter sichtbar werden. Bei IN_BORDER darf er das Gadget nicht verlassen. Die gleiche Routine wird anschließend mit horizontaler Bewegung durchgegangen. Das Erstellen eines scrollbaren Gadgets mit einigen Kindern macht man so: BlitzMax: [AUSKLAPPEN] Gadget.Create 3,0,120,360,260,0,1,"" 'Mutter für Scrollbereich Zunächst wird ein feststehendes Fenster erstellt (Mutter). Darin ein Listen-Container mit sehr großem H=760 Anschließend wird die Scroll-Eigenschaft festgelegt. Nun folgen 10 Kinder mit Text-Einträgen. Und hier wieder der fertige Code: BlitzMax: [AUSKLAPPEN] Strict |
||
Gewinner des BCC #53 mit "Gitarrist vs Fussballer" http://www.midimaster.de/downl...ssball.exe |
- Zuletzt bearbeitet von Midimaster am Do, Aug 09, 2012 20:35, insgesamt einmal bearbeitet
MidimasterBetreff: Keyboard auf dem Android |
Di, Aug 07, 2012 0:31 Antworten mit Zitat |
|
---|---|---|
Kapitel 6: Keyboard auf dem Android
nun werden wir ein Input-Gadget hinzufügen und das Programm ein erstes Mal auf einem Android-Handy testen. Dazu sind einige Vorbereitungen nötig. Um für das schnelle Testen zukünftig das Target Html5 verwenden zu können, wollen wir die Ausmaße des Browsers an unser Handy anpassen. Dazu ändern wir die von Monkey allgemein vorgegebene Canvas-Größe von 640x480 auf 320x480. Beim ersten Kompilieren hat Monkey einen Ordner MyGame.build angelegt, wo es beim Kompilieren die verschiedenen Target-Apps speichert. So gibt es nun in dem Ordner MyGame.build\html5\ eine Datei MonkeyGame.html. Wir öffnen sie mit Editpad und ändern folgende Zeile: Code: [AUSKLAPPEN] <canvas id="GameCanvas" onclick="this.focus();" oncontextmenu="return false;" width=320 height=480 tabindex=1></canvas>
Durch Einsetzen der entsprechenden Werte liese sich hier jede Handy-Display-Größe simulieren. Unsere GUI passt sich übrigens bereits immer an und wird die eigene Grafikausgabe stets formatfüllend an das Display anpassen. Die App für das Target Android legt Monkey in den Ordner MyGame.build\android\. Auch dort läßt sich bereits jetzt eine wichtige Änderung treffen: Orientation: User Im Ordner MyGame.build\html5\ öffnen wir die Datei CONFIG.TXT und ändern folgende Zeile: Code: [AUSKLAPPEN] android:screenOrientation="user" . Dies ermöglicht uns später das breitere QWERTZ Keyboard zu verwenden.
Um aus einen 0815-Gadget eine Text-Eingabebox zu machen sind folgende Schritte notwendig: Zunächst legen wir zum ersten Mal eine Kennung für TEXT_FIELD fest: BlitzMax: [AUSKLAPPEN] Strict Diese Kennung verwenden wir des besseren Übersicht nun auch immer ,wenn wir ein Textfield erstellen. also ändert sich die Create-Zeile des Gadget Nr 5: BlitzMax: [AUSKLAPPEN] Class MyGame Extends App Und beim Malen unterscheiden wir auch: BlitzMax: [AUSKLAPPEN] Class Gadget Das Textfield wird nun einen blinkenden Cursor haben, sobald es den Focus erhält. Dazu bekommt die Klasse zwei neue Variable und ein wenig code in der Check()-Funktion: BlitzMax: [AUSKLAPPEN]
Nun müssen wir nur noch die virtuelle Tastatur rufen, wenn das Textfield den Focus erhält und wir verstecken sie wieder, wenn dort die RETURN-Taste gedrückt wird. BlitzMax: [AUSKLAPPEN] Function Check%() spät... müde... fortsetzung folgt.... |
||
Gewinner des BCC #53 mit "Gitarrist vs Fussballer" http://www.midimaster.de/downl...ssball.exe |
Übersicht Andere Programmiersprachen FAQs und Tutorials
Powered by phpBB © 2001 - 2006, phpBB Group