Archiv für August 2006

FCKeditor - Stress dank eingebautem Filemanager

Freitag, 25. August 2006
Meine Fresse!
So, nachdem das gesagt ist, fühle ich mich auch gleich viel besser.  Einem Rat meines Kollegen Markus folgend, unternehme ich gerade den Versuch, den Online-WYSIWYG-Editor namens FCKeditor in mein Blog einzubasteln. Alles kein Problem; ist ja leicht genug.
Aber der Filemanager, von dem Markus bei unserem letzten Gespräch so geschwärmt hat, bekomme ich ums Verrecken nicht dazu, seinen Dienst zu verrichten. Der Filebrowser zeigt mir keine Dateien an, Uploads landen anscheinend irgendwo auf meinem Server. Noch habe ich keine der Dateien wiedergefunden, deren erfolgreichen Upload mir der Editor fröhlich bestätigt hat.
Die Dokumentation all dessen, was mit dem Filemanager zu tun hat, finde ich mächtig schmal. Zudem scheint das zugehörige Dokumentationswiki ständig überlastet zu sein. Besonders toll, wenn man nur “mal eben” einen anderen Editor installieren will.
Wenn dieser Artikel/Schmähruf in meinem Blog auftaucht, heißt das immerhin, dass der Rest vom FCKeditor etwas taugt…

Navi-Highlighting: Menüpunkt soll geklickt erscheinen

Freitag, 25. August 2006
Irgendwann wagt sich auch ein Flashneuling daran, ein Navigationsmenü für eine Flash-Website zu erschaffen. Unter Zuhilfenahme diverser Mausereignisse erreichen sie einigermaßen leicht, dass sich die einzelnen Menüeinträge verfärben, wenn BenutzerInnen sie mit dem Mauszeiger überrollen oder klicken.
Was aber oft nicht im ersten Versuch klappt, ist die Umsetzung des folgenden Plans, den bis hierhin gekommene FlashentwicklerInnen als nächstes in Angriff nehmen:
Bei Klick auf einen Menüeintrag soll dieser solange markiert bleiben, bis ein anderer geklickt worden ist. Wenn dieser Fall eingetreten ist, soll natürlich der vormals “geklickt” erschienene Menüeintrag wieder normal aussehen und sich der zuletzt geklickte nach “geklickt” hin färben oder wandeln (vgl. Beispieldatei 1).

Ziel
Dieser Artikel soll einen Weg aufzeigen, wie dieser Plan erfolgreich in die Tat umgesetzt werden kann. Nebenbei erhalten Flashneulinge vielleicht noch den einen oder anderen Tipp, wie sie ihre Navigation per Skript auf die Bühne zaubern können.

Los geht’s
Das folgende Schaubild soll illustrieren, dass zunächst lediglich ein einzelner Movieclip benötigt wird, der aus einem dynamischen Textfeld vor einem rechteckigen Hintergrund besteht:

<img width=”450″ height=”510″ alt=”" src=”http://www.christianscholz.com/img/tut_navi_highlighting.gif” />
Abb. 1: Movieclip menuItem; Abmessungen; Instanz- und Exportnamen

Es wird also in einer leeren Flashdatei ein leeres Movieclip-Symbol angelegt, welches aus den oben gezeigten Zutaten besteht. Wenn das Rechteck aufgezogen und das Textfeld geschaffen und mit dem Instanznamen tfMenuItem ausgestattet worden ist, sollte eben diesem Symbol ein Export- oder Linkagename verpasst werden, der genauso lautet wie der Symbolname: menuItem.

Im Rahmen dieses Artikels bleibt die Bühne leer; der Movieclip menuItem wird per Skript erst mehrfach auf die Bühne gebracht, während der Film läuft. Die dazu nötige Methode attachMovie() wird im folgenden Skript verwendet, wo dann auch deutlich wird, welchen Zweck der Exportname hat.

ActionScript (Frame 1 der Hauptzeitleiste)
// Anzahl der darzustellenden Menüpunkte
nAmountMenuItems = 5;

// Schleife, die bequem 5 Menüpunkte untereinander anzeigt
for (i=0; i<nAmountMenuItems; i++) {
mi = _root.attachMovie(”menuItem”, “menuItem_”+i, i);
mi._x = 50;
mi._y = i*(mi._height-1);
}

Zunächst wird in einer Variablen festgelegt, aus wie vielen Menüeinträgen die Navigation bestehen soll; hier sind es derer fünf.
Innerhalb einer FOR-Schleife wird bei jedem Schleifendurchlauf eine weitere Instanz des Movieclips, dessen Exportname menuItem lautet, an die Hauptzeitleiste gehängt, benannt und auf der Bühne positioniert.

Details zu FOR-Schleifen können <a href=”http://flash.christianscholz.com/articles/schleifen-sparen-zeit-und-muehe”>hier</a> nachgelesen werden.

Zwischenergebnis 1
Das Resultat sollten fünf sauber untereinander platzierte Menüeinträge sein, die allerdings alle den gleichen Text enthalten. Das wäre natürlich wenig vorteilhaft, wenn die Navigation irgendeinen Sinn haben soll. Daher werden ein paar Zeilen Code ergänzt, die dafür sorgen, dass jeder Eintrag einen eigenen Begriff anzeigt:

// Die Namen der Menüpunkte werden in einem Array abgelegt
arMenuHeadlines = new Array(”Home”, “Company”, “Products”, “Imprint”, “Contact”);

// Anzahl der darzustellenden Menüpunkte
nAmountMenuItems = 5;

// Schleife, die bequem 5 Menüpunkte untereinander anzeigt
for (i=0; i<nAmountMenuItems; i++) {
// Erzeugen der 5 Menüpunkte und Positionierung
mi = _root.attachMovie(”menuItem”, “menuItem_”+i, i);
mi._x = 50;
mi._y = i*(mi._height-1);

// Setze den Text im Textfeld
mi.tfMenuHead.text = arMenuHeadlines[i];
}

Ein Array speichert hier alle Texte, die auf den Menüeinträgen stehen sollen. Innerhalb der Schleife wird auf dieses Array zugegriffen und so jedem Eintrag ein anderes Arrayelement zugewiesen.

Details zu Arrays können <a href=”http://flash.christianscholz.com/articles/arrays”>hier</a> nachgelesen werden.

Er werde licht!
Als nächstes erhalten die Menüeinträge ein paar Benimm- und Verhaltensregeln. Dies geschieht ebenfalls in der Schleife, um Zeit und Platz zu sparen. Hier nur die neuen Zeilen Code; das vollständige Skript kann etwas weiter unten eingesehen werden.

mi.onRelease = function() {
this._alpha = 50;
};
mi.onRollOver = function() {
this._alpha = 50;
};
mi.onRollOut = function() {
this._alpha = 100;
};

Wird also ein Menüeintrag mit dem Mauszeiger überrollt oder geklickt, wird hier der ganze Movieclip auf halb-transparent gesetzt. Sicherlich nur für dieses Beispiel eine gute Idee. In der Regel würden sich andere Arten der Hervorhebung besser eignen.
Wird der Mauszeiger wieder weg bewegt, wird die Transparenz wieder aufgehoben.

Es ist erkennbar, dass auch der geklickte Eintrag wieder auf normale Darstellung zurückspringt, obwohl es ja wünschenswert und Ziel dieser Übung wäre, wenn er verwandelt bliebe. Um genau dies zu erreichen, wird jedem Menüeintrag eine Variable mit auf den Weg gegeben, die speichert, ob er sich in geklicktem/selektiertem Zustand befindet oder nicht: isSelected.

Ist nun ein Eintrag einmal angeklickt/selektiert worden, wird seine Variable isSelected auf den Wert true (wahr) gesetzt. Der für diesen Vorgang zuständige Skriptbereich ist der onRelease-Mouse-Handler. Hier wird (durch eine kleine Funktion namens restoreNavi()) auch geregelt, dass alle anderen Menüeinträge auf “normal” und nicht-selektiert geschaltet werden.

An anderer Stelle (beim onRollOut-Mouse-Handler) wird der Zustand des durch die Interaktion der BenutzerInnen betroffenen Menüeintrags wiederum abgefragt und entsprechend verfahren - war der Eintrag, von dem der Mauszeiger gerade wegbewegt worden ist, nicht vormals selektiert, passiert das Übliche und der Movieclip wird wieder auf “normal” geschaltet. War der Eintrag allerdings selektiert, bleibt er in seinem hervorgehobenen Zustand.

Finale und das vollständige ActionScript
Da die Menüeinträge alle ähnlich aussehen und sich ähnlich verhalten, bietet sich hier die Verwendung einer Schleife an. In anderen Szenarien mag dies nicht so naheliegend sein, aber die Technik - Speichern und Abfragen des Zustands - bleibt dieselbe.
// Die Namen der Menüpunkte werden in einem Array abgelegt
arMenuHeadlines = new Array(”Home”, “Company”, “Products”, “Imprint”, “Contact”);

// Anzahl der darzustellenden Menüpunkte
nAmountMenuItems = 5;

// Schleife, die bequem 5 Menüpunkte untereinander anzeigt
for (i=0; i<nAmountMenuItems; i++) {
// Erzeugen der 5 Menüpunkte und Positionierung
mi = _root.attachMovie(”menuItem”, “menuItem_”+i, i);
mi._x = 50;
mi._y = i*(mi._height-1);
// Jeder Menüpunkt wird auf “nicht-selektiert” gesetzt
mi.isSelected = false;
// Setze den Text im Textfeld
mi.tfMenuHead.text = arMenuHeadlines[i];
// Ereignisse, auf die jeder Menüpunkt reagieren soll
mi.onRelease = function() {
restoreNavi();
this.isSelected = true;
this._alpha = 50;
};
mi.onRollOver = function() {
this._alpha = 50;
};
mi.onRollOut = function() {
if (this.isSelected == false) {
this._alpha = 100;
}
};
}

function restoreNavi() {
for (i=0; i<nAmountMenuItems; i++) {
_root["menuItem_"+i]._alpha = 100;
_root["menuItem_"+i].isSelected = false;
}
}

Flash nervt (Teil II): Verschachtelte Event Handler

Mittwoch, 16. August 2006
Je länger ich mit Flash arbeite, desto mehr füllt sich die Liste der nervigen Schwachstellen der immerhin mehrere hundert Euro teuren Software. Arbeiten mit Flash ist wie modernes Fernsehen - immer, wenn es interessant wird, hört der Spaß plötzlich auf. Im Fernsehen ist es Werbung, die alles ruiniert, bei Flash sind es Unzulänglichkeiten, die einen ambitionierten Programmierer vor den Kopf stoßen. Wer aber schon länger mit Flash arbeitet, wird sich auch bereits daran gewöhnt haben, sich ständig Umwege ausdenken zu müssen, wie diese Hürden, die uns FlashentwicklerInnen von Herrn und Frau Macromedia-Adobe aufgebürdet worden sind, doch noch zu überwinden sind; so genannte workarounds sind an der Tagesordnung.

Das Flashfeature, eigene Kontextmenüs zu erstellen, wird ja dadurch versaut, dass sie nun gerade nicht in Standalone-Anwendungen funktionieren, aber darüber habe ich mich ja schon an anderer Stelle aufgeregt.

Worum geht es?
Ein anderes Thema, das noch mehr Leute angeht, da es häufiger nachgefragt wird als die Manipulation irgendwelcher Kontextmenüs, ist das Verschachteln von event handlers. Zur Veranschaulichung folgender Screenshot:

<img src=”../img/freel_contact_beta_shot.gif” alt=”" width=”450″ height=”147″ />
Abb. 1: Einsatzgebiet für verschachtelte event handler aus der Praxis

Es handelt sich um eine Kontaktverwaltungsapplikation (unvollendet). Interessant ist hier hauptsächlich der Bereich, wo einzelne Kontakte angezeigt werden. Jeder Kontakteintrag besteht im Wesentlichen aus einem breiten, rechteckigen Block, in dem ein Foto und einige Textinformationen untergebracht sind. Ferner gibt es noch eine kleine Checkbox. Alles zusammen steckt in einem Movieclip, der über seinen Exportnamen aus der Library auf die Bühne gebracht wird. Das Foto und die Checkbox stellen jeweils eigene Movieclips innerhalb des vorgenannten Movieclips dar.

Das Ansprechen oder Referenzieren dieser ineinander verschachtelten Movieclips ist ja kein Problem. Spannend wird es erst, wenn ich folgenden Plan in die Tat umsetzen will:

  • Bei Überrollen des Blocks soll die Meldung “Block überrollt” ausgegeben werden.
  • Bei Klick auf das Foto soll die Meldung “Foto geklickt” ausgegeben werden.
Versuch 1:
block.onRollOver = function () {
trace(”Block überrollt”);
}
block.foto.onRelease = function () {
trace(”Foto geklickt”);
}

Ergebnis von ActionScript-Versuch 1:
Der event handler onRollOver wird wie gewünscht ausgeführt, die Meldung erscheint. Auf das Foto kann allerdings geklickt werden, bis die Maustaste ihren Geist aufgibt - nichts passiert, keine Meldung erscheint im Ausgabefenster. Das liegt daran, dass es in Flash (noch?) nicht ohne Weiteres möglich ist, event handler auf diese Weise zu verschachteln. Das Skript für den Eltern-Movieclip “block” überdeckt das Skript für den Kind-Movieclip “foto”.

ActionScript-Versuch 2:
block.onRollOver = function() {
trace(”Block überrollt!”);
delete this.onRollOver;
};
block.foto.onRelease = function() {
trace(”Foto geklickt”);
};

Ergebnis von ActionScript-Versuch 2:
Der event handler onRollOver wird ausgeführt, onRelease ebenfalls.

Wird also das überdeckende Skript nach dem ersten Inkrafttreten entfernt (per delete), erhält das zunächst überdeckte Skript eine Chance, seinen Dienst zu verrichten.

Wird jetzt allerdings erneut der Block überrollt, passiert nichts weiter, denn sein Skript ist ja entfernt worden. Das ist natürlich ungünstig, da BenutzerInnen natürlich ruhig so oft mit dem Mauszeiger über den Block rollen können sollen, wie sie wollen, ohne Funktionalitätseinbußen in Kauf nehmen zu müssen.

Irgendwie muss ich also das ursprüngliche Verhalten des Blocks wieder herstellen.

ActionScript-Versuch 3:
block.onRollOver = function() {
trace(”Block überrollt!”);
delete this.onRollOver;
};
block.foto.onRelease = function() {
trace(”Foto geklickt”);
this._parent.onRollOver = function() {
trace(”Block überrollt!”);
delete this.onRollOver;
};
};

Ergebnis von ActionScript-Versuch 3:
Teilerfolg! Der event handler onRollOver wird aktiv, onRelease ebenfalls. Leider befindet sich der Mauszeiger noch über dem Block (eigentlich über dem Foto, aber damit auch über dem Block), während sein Skript restauriert wird, so dass es sofort wieder ausgelöst - und gelöscht - wird.

Wenn ich die Wiederherstellung des Block-Skripts nicht mit onRollOver des Blocks koppele, sondern mit dessen onRollOut, …

ActionScript-Versuch 4:
block.onRollOver = function() {
trace(”Block überrollt!”);
delete this.onRollOver;
};
block.foto.onRelease = function() {
trace(”Foto geklickt”);
this._parent.onRollOut = function() {
trace(”Mauszeiger von Block weggerollt!”);
_root.block.onRollOver = function() {
trace(”Block überrollt!”);
delete this.onRollOver;
delete this.onRollOut;
};
};
};

Ergebnis von ActionScript-Versuch 4:
…komme ich der Lösung zwar ein Stückchen näher und das ursprüngliche Überroll-Verhalten des Blocks wird wieder hergestellt, aber nur(!), wenn vorher das Foto angeklickt worden ist. Wenn BenutzerInnen aber den Block überrollen, das Foto keines Blickes/Klicks würdigen und wieder aus dem Block herausrollen, habe ich immer noch dasselbe Problem: Der Block bleibt deaktiviert und regungslos zurück wie ein kurzgeschlossener Toaster. Füge ich statt dessen einen onRollOut event handler vor allem anderen ein, lande ich wieder in derselben Falle wie ganz am Anfang:

ActionScript-Versuch 5:
block.onRollOut = function() {
trace(”Mauszeiger von Block weggerollt!”);
};
block.onRollOver = function() {
trace(”Block überrollt!”);
delete this.onRollOver;};
block.foto.onRelease = function() {
trace(”Foto geklickt”);
this._parent.onRollOut = function() {
trace(”Mauszeiger von Block weggerollt!”);
_root.block.onRollOver = function() {
trace(”Block überrollt!”);
delete this.onRollOver;
delete this.onRollOut;
};
};
};

Ergebnis von ActionScript-Versuch 5:
Das Foto-Skript wird wieder überdeckt, diesmal nicht vom onRollOver-, sondern vom onRollOut-Skript des Blocks.

Nicht nur, dass ich auf diese Weise offenbar nicht auf einen grünen Zweig komme; das Skript wird auch noch abartig unübersichtlich und unhandlich.

Anderer Ansatz - hitTest()
Kramt man etwas in Flashs Trickkiste, findet man eine Methode, die ich persönlich genauso halbherzig umgesetzt finde, wie so vieles andere in Flash: hitTest(). Sie soll FlashentwicklerInnen bei Kollisionsabfragen unterstützen, also Abfragen erlauben wie zum Beispiel “Überschneidet sich Movieclip A mit Movieclip B?” oder “Befindet sich der Mauszeiger über Movieclip X?”.

Der Gedanke bei dem folgenden Versuch ist einfach: Wenn man keine event handler verschachteln kann, die speziell mit Maus-Interaktion zusammen hängen, gelingt dann vielleicht die Verschachtelung aus so einem event handler und einer hitTest-Abfrage?

ActionScript-Versuch 6:
function over() {
trace(”Block überrollt!”);
};
block.onRollOver = over;block.foto.onEnterFrame = function() {
if (this.hitTest(_root._xmouse, _root._ymouse)) {
delete this._parent.onRollOver;
this.onRelease = function() {
trace(”Foto geklickt”);
};
} else {
this._parent.onRollOver = over;
}
};

Ergebnis von ActionScript-Versuch 6:
Das Skript ist nicht hübsch, aber es funktioniert. Dem Foto-Movieclip wird ein onEnterFrame event handler mit auf den Weg gegeben, der x mal pro Sekunde überprüft, ob sich der Mauszeiger über dem Foto befindet. Falls dies zutrifft, wird schnell ein geeigneter event handler für das Foto ins Leben gerufen und der event handler für den Block entfernt. Falls BenutzerInnen jedoch das Foto gar nicht im Visier haben, erhält der Block sein normales Verhalten (zurück, wenn das Foto berührt worden ist).

Eigentlich unlogisch
Im Grunde wundert mich, dass es auf diese Weise funktioniert, denn laut Flash-ActionScript Dictionary handelt es sich auch bei onEnterFrame um einen event handler, das heißt, wir haben es hier eigentlich wie bei den ganzen gescheiterten Versuchen oben auch mit sich überdeckenden event handlers zu tun. Nur dass es hier läuft… *seufz*