Lade...
 

MorphIT Layouting

MorphIT Layouting

Diese Seite beschreibt die Funktionsweise des aktuellen Layouting-Mechnismus der MorphIT-Weboberfläche. Ziel von MorphIT war es eine Web-Oberfläche zu schaffen, in der die meisten nativen Anwendungen ohne weitere Anpassung laufen. Dies ist jedoch Aufgrund der Unterschiede zwischen nativer Oberfläche und Web-Oberfläche nicht immer möglich. Um Anwendungen zu schreiben, die sowohl nativ, als auch im Web gut aussehen ist es notwendig zu verstehen, welche zusätzlichen Regeln und Einschränkungen für die Web-Oberfläche gelten.

 

Konzept: Fenster

Die aktuelle MorphIT Weboberfläche zeigt immer das aktive Fenster als Weboberfläche. Da mehrere Fenster im Gegensatz zu nativen Oberflächen im Web eher als störend empfunden werden und auf kleinen Geräten nicht effektiv damit gearbeitet werden kann, stellt MorphIT nur das aktive Fenster als Weboberfläche. Das Ziel einer guten ClassiX-Anwendung sollte also auch sein, dass man einen Anwendungsfall über ein Fenster möglichst ohne umschalten auf andere Fenster bearbeiten kann. Sollten mehrere Fenster in ClassiX geöffnet sein, dann wird nur das aktuell aktive Fenster dargestellt. Von Hintergrundfenstern bekommt der Nutzer nichts mit. 

Um dem Nutzer einen Auswahldialog anzubieten ohne das gesamte Fenster zu wechseln, unterstützt MorphIT modale Fenster und Floatfenster. Diese werden als verschiebbare und schließbare Fenster über der aktuellen Oberfläche gezeichnet.

Konzept: Toplevel Gruppen/Composites

Im folgenden Text werden Gruppen und Composites synonym verwendet.
Der wichtigste Unterschied zwischen nativer Oberfläche und Web-Oberfläche ist die verfügbare Größe.

Native Oberflächen werden für Fenster definiert, die üblicher Weise für Desktop-Anwendungen gelayouted werden und deren Inhalt über Attachments and eine Größenänderung des Fensters anpassen können. Die Fenster sind meist so gestaltet, dass die gesamte Information auf auf den Desktop passt. Scrollen von Fenstern ist in nativen Oberflächen eher unüblich. Die Schrift richtet sich (falls nicht anders konfiguriert) nach der in Windows üblichen Schriftgröße für Oberflächenelemente.

Eine Web-Oberfläche muss sowohl auf einem Desktop, als auch auf einem Tablet oder Smartphone gut bedienbar sein. Um dies zu ermöglichen, werden größere Schriften verwendet und Oberflächencontainer werden an die verfügbare Breite angepasst und bei Bedarf untereinander dargestellt (siehe: Responsive Design). Ein Kernkonflikt zwischen nativer Oberfläche und Web-Oberfläche stellt die Höhe der Oberfläche dar. Nativ wird sie durch die Fensterhöhe vorgegeben und die Elemente müssen sich an die verfügbare Höhe anpassen und im Web sollten die Elemente in ihrere Summe die Gesamthöhe eines Fensters bestimmen.

Gruppenbreite

Die Responsiveness des Web-Layouts wird dadurch unterstützt, dass alle Toplevel-Gruppen als responsive Container interpretiert werden, die in einem 6-Spalten-Grid je nach Verhältnis zwischen Breite der Gruppe (im InstantView-Sourcecode) und Breite des enthaltenen Fensters (im InstantView-Sourcecode) eine festgelegte Anzahl an Spalten einnehmen. Eine Toplevel-Gruppe kann also die Breiten 16%, 33%, 50%, 66%, 83% und 100% der gesamten Fensterbreite einnehmen. Bei der Breitenberechnung werden Attachments der Toplevel-Gruppe an das Parent-Fenster berücksichtigt, sodass die Gruppe aus folgendem Codebeispiel trotz einer Breite von 0 durch die Attachments das gesamte Fenster einnehmen wird:

Window(window, 0, 0, 350, 100, "Parent Window") { Group(group, 0, 0, 0, 0, "The group") Attach(group, LEFT, RIGHT, STRETCH) }

 Sollte der Browser kleiner gezogen werden, dann erhöht sich die Anzahl der eingenommen Spalten pro Gruppe, sodass irgendwann auch die kleinsten Gruppen die gesamte Fensterbreite einnehmen und die Gruppen umbrechen und untereinander dargestellt werden.

 

Wichtig: An dem Beispiel zeigt sich ein weiterer Punkt, der beachtet werden muss. Das Fenster ist mit 350 Minicells (~245 Pixel) nativ recht schmal gewählt und die Gruppe wäre nativ damit auch nur 245 Pixel schmal, was auf einem 1920 Pixel breiten Bildschirm sehr klein wirkt. Für die Gruppenbreite in MorphIT zählt jedoch nur das Verhältnis zwischen Fenstergröße und Gruppengröße, sodass die Gruppe in dem Fall 100% der verfügbaren Fensterbreite erhält und damit in MorphIT auf einem vollen Bildschirm etwa 1200 Pixel breit dargestellt wäre. Eine so kleine Gruppe, die durch Attachments auf eine solche Größe gestreckt wird, kann durchaus etwas unnatürlich wirken und schwer zu layouten sein.
Im Web-Layout sind bei sehr großen Bildschirmen etwa 1270 Pixeln für die Fensterbreite verfügbar. Damit die Gruppen auch in absoluten Größen den nativen Gruppenbreiten im Fenster entsprechen, müsste das Fenster 1815 Minicells breit gewählt werden.
 

 

Toplevel-Gruppen sind alle Gruppen und Composites, die entweder direkt (ohne umschließende Gruppe) in einem Fenster enthalten sind oder in einem Notebook-Fenster, welches wiederum direkt in einem Fenster liegt. Ob MorphIT eine Gruppe als Toplevel erkannt hat, kann man im Browser kontrollieren, indem man das Gruppen-Element untersucht und prüft, ob die CSS-Klasse toplevel für das Element vergeben wurde.
Durch die Verwendung der Spalten für das Responsive-Design im Web werden für Toplevel-Gruppen die Positionsanagaben im InstantView-Code ignoriert, das sich eine feste Positionierung und ein Responsive-Design an der Stelle konzeptionell widersprechen. Die Reihenfolge der Gruppen ergibt sich aus der Reihenfolge in der die Gruppen in InstnatView definiert wurden.

Dies ist am folgenden Beispiel verdeutlicht:

Window(Layout2, 0,0, 800, 300, "Layout 2") { Group(g1, TOGGLE, 400, 0, 400, 100, "400,0,400,100") { Group(g11, 0, 10, 200, 90, "0,20,200,100") Group(g12, 200, 10, 200, 90, "200,20,200,100") } // g2 is placed before g1 using absolute coordinates (this is ignored in web) Group(g2, TOGGLE, 0, 0, 400, 200, "0,0,400,200") { Group(g21, 0, 10, 400, 90, "0,10,400,90") Group(g22, 0, 100, 400, 90, "0,100,400,90") } }

Das TOGGLE-Flag dient dazu, dass die Gruppe im Web einklappbar ist, sodass der Nutzer durch Einklappen der von Gruppen eine lange Oberfläche auf die notwendigen Elemente reduzieren kann. Dieser Zustand lässt sich auch vom InstantView-Entwickler über den MorphIT-Slot isHidden steuern.

Natives Layout
Natives Layout
Web-Layout (groß)
Web-Layout (groß)
Web-Layout (klein)
Web-Layout (klein)
Web-Layout (klein & zusammengeklappt)
Web-Layout (klein & zusammengeklappt)

Die Toplevel-Gruppen sind in den Beispielen grün eingefärbt und die Sublevel-Gruppen blau. An den Beispielen erkennt man auch ganz gut die nächste Einschränkung der Web-Oberfläche. Die verwendete Schriftgröße der Gruppenüberschriften ist fast doppelt so groß wie die übliche native Gruppenüberschrift. Diese Schriftgröße ist durch das Design vorgegeben und dient der besseren Orientierung auf der Weboberfläche und damit der Usability. Hinzu kommt, dass die Gruppe Platz lassen muss für den TOGGLE-Button, der nativ nicht existiert.
Gruppenabstände und Größen sollten nativ also etwas größer gewählt werden als zwingend notwendig, damit die Web-Layouts bedienbar bleiben.

Eine ähniche Schriftgröße für Gruppenüberschriften ließe sich mit folgendem Code setzen:

3 [ 17 ] MSFonts

Hierdurch erkennt man recht schnell, ob eine Maske auf große Gruppenüberschriften ausgelegt ist oder nicht. Aktuell ist das setzen der größeren Gruppenüberschrift keine Lösung für den Layoutmismatch zwischen nativ und MorphIT, weil die Gruppen die Y-Position für ihre Unterwidgets anders berechnen.

Gruppenhöhe

Die Gruppenhöhe einer Toplevel-Gruppe ergibt sich direkt aus dem InstantView-Sourcecode. Vertikale Attachments der Gruppe zum darüberliegenden Fenster werden für die Gruppenhöhe ebenfalls berücksichtigt. Vertikale Attachments der Toplevel-Gruppen untereinander werden nur für die initiale Größe berücksichtigt. Da die Weboberfläche beliebig weit vertikal scrollen kann, beeinflusst die Höhe des Browser-Fensters in keiner Weise eine per Stretch-Attachment in der Höhe dynamische Gruppe. Einzige Ausnahme hiervon sind modale und Floatfenster, da sie eine (initial) begrenzte Höhe haben.

Top-Level-Gruppen mit Headern und dynamischer Höhe (STRETCH) in modalen und Floatfenstern führen in der aktuellen MorphIT-Version zu Layout-Problemen, die erst das nächste Positionierungs-Engine lösen wird.

Dynamische Gruppenhöhe

Da es im Web üblich ist, dass der Inhalt die Höhe des gesamten Fensters bestimmt und Listen eher über das Fenster gescrollt werden, anstatt für jede Liste im Fenster einen eigenen Scrollbalken zu haben, wurde unter MorphIT für die Listen-Widgets (ObjectListView & TreeList) sowie für WebWidgets die Möglichkeit eingebaut, dass diese Widgets ihre Größe an ihren Inhalt anpassen können. Damit das geht, muss das Widget selbst und alle parent-Gruppen bis zur Toplevel-Gruppe Vertikal-Stretch-Attached an den jeweiligen Parent sein. Die Toplevel-Gruppe muss nicht mehr stretch attached sein. Ist diese Bedingung erfüllt, dann passt das Widget die Höhe der Toplevel-Gruppe so an, dass sich der gesamte Inhalt ohne Scrollen erreichen lässt. Sollte die Bedingung nicht erfüllt sein, dann wird wie nativ auch ein Scrollbalken in dem Listen-Widget dargestellt.
Die Listenhöhe aus dem Sourcecode gibt dabei die Mindesthöhe der Liste an. Eine leere Liste wird also nie unter diese Höhe schrumpfen.

Das Verhalten wird in folgendem Beispiel verdeutlicht:

Window(Layout3, 0,0, 600, 100, "Layout 3") { Menu { Item("Fill lists") [ SELECT: FillLists ] } // Group and list stretch attached to bottom Group(g1, 0, 0, 200, 100, "bottom stretch list") { ObjectListView(list1, AUTO_POSITION, 0,10,0,0) [INITIALIZE: PrepareList ] Attach(list1, LEFT, RIGHT, BOTTOM, STRETCH) } // Only group stretch attached to bottom Group(g2, 200, 0, 200, 100, "no stretch list") { ObjectListView(list2, AUTO_POSITION, 0,10, 0, 90) [INITIALIZE: PrepareList ] Attach(list2, LEFT, RIGHT, STRETCH) } // Only List stretch attached to bottom Group(g3, 400, 0, 200, 100, "no stretch group") { ObjectListView(list3, AUTO_POSITION, 0,10,0,0) [INITIALIZE: PrepareList ] Attach(list3, LEFT, RIGHT, BOTTOM, STRETCH) } Attach(g1, BOTTOM, STRETCH) Attach(g2, BOTTOM, STRETCH) }

 

Ohne Daten
Leere Listen nativ
Leere Listen nativ
Leere Listen im Web (groß)
Leere Listen im Web (groß)
Leere Listen im Web (klein)
Leere Listen im Web (klein)
Mit Daten
Befüllte Listen nativ
Befüllte Listen nativ
Befüllte Listen im Web (groß)
Befüllte Listen im Web (groß)
Befüllte Listen im Web (klein)
Befüllte Listen im Web (klein)

Auch hier sind die Toplevel-Grupen zum besseren Verständnis grün eingefärbt.

 

Eine Sonderstellung der dynamischen Gruppenhöhe nimmt der Richtext-Editor ein. Dieser passt seine Höhe nicht an den Inhalt an, sondern lediglich an die vom Benutzer eingestellte Größe - der Editor lässt sich an der unteren, rechten Ecke mit der Maus großziehen.

 

Layouting von Childwidgets

Innerhalb von Toplevel-Gruppen gilt aber wieder die absolute Positionierung, für welche die (X,Y,Breite,Höhe)-Angabe der Widgets in Pixel umgerechnet und im Browser dargestellt wird. Das Attachment-Layouting funktioniert hier ebenfalls, aber das LAYOUT-Flag wird nicht unterstützt!

Die Gruppenüberschrift ist Teil der angegebenen Gruppenhöhe von Sublevel-Gruppen. Dadurch nimmt die größere Gruppenüberschrift im Web dem Gruppeninhalt im Vergleich zur nativen Oberfläche mehr Platz weg. Dies muss beim Positionieren von Sublevel-Gruppen berücksichtigt werden, damit sich diese nicht überlappen. Bei Toplevel-Gruppen zählt die Höhe der Gruppenüberschrift nicht zur Gruppenhöhe. 

Die Positionsangabe der Widgets innerhalb der Gruppen wird von der nativen Oberfläche auf die Weboberfläche so umgerechnet, dass soweit möglich der Gruppeninhalt Pixelngenau mit dem nativen Gruppeninhalt übereinstimmt. Wird die Gruppe durch verkleinern des Browsers kleiner, dann passen sich beim Attachment natürlich auch die Größen/Positionen der enthaltenen Wigets an die neue, verfügbare Größe an.

Layout von Notebooks

Für Notebooks gibt es beim Layouting einige Sonderregeln, da sie häufig dazu verwendet werden, um mehrere Fenster in einem einzigen Fenster darzustellen, was vor allem in MorphIT, welches nur ein Fenster zur Zeit anzeigt, ein Hilfmittel ist, um komplexe Oberflächen zu gliedern. 

Notebooks nehmen ohne Angabe von Koordinaten nativ, sowie im Web die gesamte Größe ihres Containers ein, füllen also das gesamte Fenster oder die enthaltende Gruppe aus.

Wird die Größe und Position von Notebooks angegeben, dann wird diese nativ zwar beachtet, aber in MorphIT komplett ignoriert. Notebooks füllen immer die gesamte Breite des enthaltenden Fensters (oder Gruppe) aus und bei mehreren Notebooks in einem Fenster werden die Notebooks untereinander dargestellt. Da Notebooks unterschiedliche große Inhalte enthalten können wird die Höhe der Notebooks durch die Höhe des Inhalts der aktuell aktiven Notebook-Lasche vorgegeben. Falls das Notebook in einer Gruppe liegt, welche die Höhe des Notebooks einschränlt, dann lässt sich der Inhalt über einen eingeblendeten Scrollbalken scrollen.

Gruppen innerhalb von Notebooks

Werden Gruppen in einem Notebook platziert, das direkt in einem Fenster liegt, dann werden diese Gruppen ebenfalls als Toplevel-Gruppen behandelt und passen sich damit an die Fenstergröße an, brechen um und können sich in der Höhe an den Inhalt anpassen. Das heißt aber auch im Umkehrschluss, dass die Position der Gruppen innerhalb eines Notebooks normaler Weise ignoriert wird. Dies gilt auch für verschachtelte Notebooks, solange kein Notebook in einer Gruppe enthalten ist. Im Nachfolgenden Beispielcode ist die Gruppe group in einem Notebook enthalten, das wiederum in einem Notebook enthalten ist. Da keines der Notebooks in einer Gruppe liegt, wird die Gruppe trotzdem als Toplevel-Gruppe gelayouted.

Window(Layout2, 0,0, 800, 300, "Layout 2") { Notebook { Window(win1, 0, 0, 0, 0, "outer") { Notebook { Window(win2, 0,0,0,0,"inner") { Group(group, 0,0,800,300, "toplevel group") { Button(bt1, 10, 10, 200, 10, "some button") } } } } } }

 

Liegt eine Gruppe in einem Notebook, das wiederum innerhalb einer Gruppe liegt, dann wird die Gruppe nicht mehr als Toplevel-Gruppe gelayouted und ihre Position und Größe werden korrekt berücksichtigt. 
Beispiel:

Code Ergebnis
Window(Layout2, 0,0, 800, 200, "Layout 2")
{
  Group(top, 0, 0, 800, 200, "toplevel")
  {
    Notebook
    {
      Window(win1, 0,0,0,0, "page")
      {
         Group(sub, 400, 50, 400, 100, "sublevel")
         {
           Button(bt1, 10, 10, 200, 10, "some button")
         }
      }
    }
  }
}
Eine Gruppe in einem Notebook in einer Gruppe
Eine Gruppe in einem Notebook in einer Gruppe

Auch hier sind Toplevel-Gruppen zum besseren
Verständnis grün eingefärbt und Sublevel-Gruppen blau.

 

Ein Notebook wird vom Layout-Mechanismus wie eine Vertikale-Stretch-Attachte Gruppe behandelt, sodass bei folgendem Layout:

  • Gruppe
    • Notebook
      • Window (Tab)
        • Gruppe
          • Liste

sich die Liste in der Höhe an die Anzahl der darzustellenden Elemente anpassen kann, falls die innere Gruppe vertikal an das enthaltene Fenster stretched.

Weitere Besonderheiten

Passwortfelder

Strings mit dem Flag PASSWORD werden in MorphIT ebenfalls als Passwortfeld dargestellt, erhalten aber innerhalb des Widgets ein kleines Icon (25px breit) mit dem sich das Passwort für die Barrierefreiheit im Klartext anzeigen lässt. Das heißt, dass ein Passwortfeld auch nativ ausreichend breit gewählt sein sollte, damit der fehlende Platz die Nutzung des Feldes nicht beeinträchtigt.

Datumsfelder

Date-Widgets erhalten in MorphIT automatisch einen Date-Picker. Dieser Date-Picker ist etwa 30 Pixel breit und wird von der Breite des Eingabefelds des Date-Widgets abgezogen. Damit ein Date-Widget in MorphIT bedienbar ist, darf es nativ nicht gerade so groß genug sein, um ein Datum einzugeben, sondern sollte etwas breiter gewählt werden.

Mehrsprachige Texte

In MorphIt besteht die erste Zeile des MLText-Widgets nur aus der Sprachauswahl. Sollte also nativ ein MLText-Widget von der Höhe auf eine Zeile abgestimmt sein, dann wird die Höhe des Widgets in MorphIT nicht ausreichen. Für einzeilige, mehrsprachige Eingaben sollte entweder MLString verwendet werden oder die Höhe des MLText-Widgets etwas größer gewählt werden.

Richtext-Editor

Wird ein Text-Widget über den MorphIT-Slot morphIt.description als Richtext-Editor dargestellt, dann muss hier berücksichtigt werden, dass der Editor eine Toolbar enthält, die den editierbaren Bereich um etwa 45 Pixel vertikal reduziert und das entsprechende Text-Widget sollte ausreichend hoch gewählt werden.

 

 

Zusammenfassung der Einschränkungen

  1. Das LAYOUT-Flag wird nicht unterstützt.
  2. Die Positionsangabe von Toplevel-Gruppen wird ignoriert.
  3. Die Breite von Toplevel-Gruppen ist relativ zur Fensterbreite und nicht absolut.
  4. Gruppenüberschriften sind fürs Web deutlich größer und die Gruppe muss höher/breiter gewählt werden als nativ nötig wäre.
  5. Vertikales STRETCH-Attach wird bei Toplevel-Gruppen nur für die Bestimmung der Initialgröße verwendet. Das Browserfenster zu vergrößern, verändert die Höhe solcher Gruppen nicht.
  6. Position und Größe von Notebooks wird nicht berücksichtigt.
  7. Passwortfelder und Datumsfelder sollten im Web etwas breiter gewählt werden.
  8. MLText und Richtext sollten im Web etwas höher gewählt werden.
  9. Modale und Floatfenster sollten keine attachted Toplevel-Gruppen mit Überschriften haben.

 

Layout visualisieren

Um Layoutfehler einfach und schnell zu finden, kann das folgende Browser-Bookmarklet als Bookmark verwendet werden, was den hier verwendeten Stil aktiviert. Dadurch erhalten alle Gruppen einen Rahmen und Toplevel-Gruppen werden grün markiert und Sublevel-Gruppen blau.

javascript:(function(){ if (document.getElementById('debug-style')) { document.getElementById('debug-style').remove(); } else { var style=document.createElement('style'); style.id='debug-style'; style.innerHTML=".toplevel.GROUP,.toplevel.COMPOSITE,.toplevel.GROUP:last-of-type,.toplevel.COMPOSITE.last-of-type{border:1px solid black;background-color:rgba(0,255,0,0.2)}.sublevel.GROUP,.sublevel.COMPOSITE{border:1px solid black;background-color:rgba(0,0,255,0.2)}.GROUP>.GroupContentWrapper,.COMPOSITE>.GroupContentWrapper{background-color:transparent}"; document.head.appendChild(style); } })();