qua API
Nach mehreren Monaten Entwicklung ist es endlich so weit: Mehr als 2 Jahre nach meiner ersten Lycodon-API erscheint heute ein würdiger Nachfolger. Die qua API bietet in verbesserter Form bereits bekannte Features wie Buttons und Markups, gleichzeitig gibt es aber noch deutlich mehr neue Inhalte. Zunächst werde ich wichtige Grundlagen erklären, und anschließend anhand von einfachen Beispielen die Nutzung der API verdeutlichen.
Grundlagen
Was ist eine API?
Eine API ist eine Schnittstelle, über die man fremden Code leicht in eigenen Code einbauen kann. Durch die Verwendung von APIs wird der eigene Code einfacher und damit übersichtlicher. Ein API-Aufruf kann vorhandenen Code ersetzen und damit verkürzen, oder völlig neue Funktionalität hinzufügen.
Download
Die qua API ist für jeden offen auf GitHub verfügbar. Damit man nicht alle Dateien manuell herunterladen muss gibt es einen Installer auf Pastebin.
Import
Module der API werden mittels require importiert, da os.loadAPI einige Nachteile mit sich bringt (siehe Anhang). Meine Implementierung von require orientiert sich an der Funktionalität, die standardmäßig in Lua eingebaut wäre. Damit mein require von überall aufrufbar ist muss es in _G.require geladen werden (mein Installer erstellt automatisch ein Startup für diese Aufgabe). Größter Unterschied zu os.loadAPI ist die Angabe des Pfades mit Punkten anstelle von Slashes (zB "qua.core.assert" statt "apis/qua/core/assert"). Die Imports sind mit require meist kürzer, da an mehreren Orten gesucht wird, und gesamte Ordnerstrukturen importiert werden können. Als Extremfall funktioniert bspw. auch Folgendes: local qua = require "qua"
Hinweise:
- An folgenden Pfaden werden Dateien von require gefunden: relativ zum aufrufenden Code, absolut in "apis", absolut in "rom/apis"
- require findet genau wie os.loadAPI ebenfalls Syntaxfehler in importiertem Code.
- Nach Veränderung oder Update einer geladenen API wird ein Neustart des Computers benötigt - ansonsten wird der vorherige Inhalt aus dem Cache importiert.
- require funktioniert nicht nur für qua - damit können auch die OS-APIs (require "textutils") oder eigene APIs importiert werden!
Klassen
Fast alle Module tragen ihre Funktionalität in Klassen. Da Lua per se keine Objektorientierung unterstützt beinhaltet qua in "qua.core.class" einen eigenen Prototyp für Klassen. Dieser Beitrag soll nicht Objektorientierte Programmierung erklären, deshalb hier nur wie die wichtigsten Konzepte umgesetzt sind:
Private Werte: Alle Keys die mit "_" beginnen
Anwendung
Optionale Parameter stehen in eckigen Klammern.
UI
Die grafischen Elemente sind derzeit der wichtigste Bestandteil der API. Sie werden alle durch den Aufruf von draw() gerendert. Die Beispiele könnt ihr auf einem 2x2 Monitor mit Scale 1 selbst ausprobieren & verändern.
Elemente
Sind Klassen zum Rendern von häufig benötigten grafischen Komponenten. Können alleine verwendet werden, Container erleichtern aber die Arbeit mit mehreren Elementen.
Label
- Einfarbiger und einzeiliger Text
Box
- Farbiges gefülltes Rechteck
Markup
- Ähnlich zum Label, aber mit änderbarer Farbe
- Farbänderungen werden im Text durch Codes ausgedrückt
- Jeder Text nach einem Code hat bis zum nächsten Code diese Farbe
- Die Codes haben die Form &[textcolor][backgroundcolor]
- Sollte ein & im Text vorkommen müsst ihr es als <AND> verschlüsseln
- Farbcodes reichen von 0-F (Reihenfolge entspricht den Woll-Farben)
Image
- Darstellung aus farbigen Pixeln
- Derzeit werden nur Bilder aus dem CC-Paint unterstützt: img:fromPaint(path)
Button
- Farbiges Rechteck mit zentriertem Label und Verhalten beim Anklicken
- Verhalten wird durch setAction(func, ...) definiert
- Beim Klick wird dann func(...) aufgerufen
- Achtung: Die Parameter werden nicht erst beim Klick evaluiert! myButton:setAction(print, os.time()) versus myButton:setAction(function() print(os.time()) end)
Window
- Simuliert Bild-im-Bild für Freihand-Anweisungen ohne Drawables
- Unterstützt alle Aufrufe von "echten" Monitoren
- Anweisungen müssen mit dem Fake-Monitor ausgeführt werden
- Diese Anweisungen beeinflussen dann nur das Window, nicht den umliegenden Bildschirm
Container
Container selbst werden nicht gerendert. Ihr Zweck ist das Gruppieren von vielen einzelnen Elementen für leichtere Handhabung & kürzeren Code.
Screen
- Einfache Sammlung von Elementen auf einem Bildschirm (stellt es euch vor wie ein <div>-Tag in HTML)
- Elemente können statisch oder dynamisch hinzugefügt werden (Beispiel 1):
1) Statische Elemente werden nur einmalig gerendert, und verbleiben dann so auf dem Screen
2) Dynamische Elemente können sich ändern, und werden daher zusammen mit dem Screen gerendert
- Je früher ein Element hinzugefügt wurde, desto tiefer ist auch seine Ebene (Beispiel 2): "Wer zuerst kommt malt zuerst"
MultiScreen
- Sammlung von mehreren Screens zum schnellen Wechsel zwischen ihnen
- Screens werden namentlich hinzugefügt und gewählt: myMultiScreen:addScreen(name, myScreen); myMultiScreen:selectScreen(name)
- Es gibt eine Abkürzung um an einen leeren Screen in maximaler Größe erzeugen, der schon automatisch hinzugefügt ist: local myScreen = myMultiScreen:getNewScreen(name)
Display
- Wrapper um einen MultiScreen zur einfachen Verwendung als Wurzel-Element
- Rendert auf ein "echtes" Terminal (term oder Monitor-Peripheral) in voller Größe
- Sollte fast immer die Grundlage einer Anwendung mit Drawables sein
- Vorteile:
1) immer volle Größe (vs. manuell im MultiScreen-Konstruktor)
2) Merkt sich das Terminal (vs. manuell bei jedem draw-Aufruf)
3) Automatisches Update beim Screen-Wechsel (vs. manuell mit draw())
Animation
- Zeitlich geordnete Abfolge von mehreren Elementen
- Nimmt bei jedem Draw-Vorgang das Aussehen vom jeweils nächsten Element an
- Der Wert looping bestimmt das Verhalten wenn kein "nächstes Element" mehr vorhanden ist:
false: Das letzte Element wird weiterhin dargestellt (neue Elemente können weiterhin angefügt werden!)
true: Die Animation springt zum ersten Element zurück und beginnt erneut
Sonstige
Die übrigen Bestandteile sind nur kurz aufgelistet. Falls ihr ein konkretes Problem mit einem dieser Module habt, fragt mich bitte.
Weitere komplexere Beispiele findet ihr auf meiner Pastebin-Seite. Wenn Ihr Fragen und Probleme habt - meldet Euch bei mir! Verbesserungs- und Änderungsvorschläge nehme ich auch immer gerne an. Wenn meine API sinnvoll nutzbar ist und funktioniert wie sie soll haben wir alle mehr davon
MFG Qivex