Programmierung

Skalierung von Dialogen

In der IT-Welt ist der technische Fortschritt sicher nichts Neues. Dieser findet in allen Bereichen statt, nicht nur bei der unterschiedlichsten Hardware, sondern auch bei den Bildschirmauflösungen. Anfang der neunziger Jahre lag die am weitesten verbreitete Auflösung bei 640×480 Pixeln. Ende des Jahrzehnts lag diese bereits bei 800×600 Pixel. Die Auflösung erhöhte sich im Laufe der Jahre weiter von 1024×768 bis zur akutellen Darstellung von 1366×768 Pixel. Infolgedessen gab es beim Support in der Vergangenheit vermehrt Anfragen, wie eine individuelle Skalierbarkeit eines Frames in CONZEPT 16 umgesetzt werden kann. Im folgenden Artikel möchten ich Ihnen eine Prozedur vorstellen, welche einen einfachen Weg bietet, vorhandene Frames individuell zu skalieren.


Die Grundidee:

Meist arbeiten die Anwender im Desktopbereich mit den üblichen Auflösungen 1024 x 768, 1366 x 768, 1280 x 1024 und 1920 x 1080.
Anstatt nun den zugrunde liegenden Dialog zur Laufzeit zu skalieren, werden für die bekannten Auflösungen skalierte Dialoge generiert und mit WinSave() in der Datenbank gespeichert. Wenn dann der Dialog in der Anwendung angezeigt werden soll, wird zunächst die Auflösung des Bildschirms bestimmt und anschließend der vorgenerierte Dialog geladen. Dies kann mit den Standard CONZEPT 16-Befehlen WinInfo() und WinDialog() erreicht werden. Bliebe noch die Aufgabe der Skalierung, die Gegenstand des vorliegenden Artikels ist.

Main-Funktion:

Innerhalb dieser Funktion wird der bereits existente, zu skalierende Frame übergeben. Als nächstes kann nun die gewünschte Größe angegeben werden. Hier ist zu empfehlen, dass das Seitenverhältnis des skalierten Frames identisch zum Ausgangsdialog ist, da es sonst zu einer nicht gewünschten Darstellung, von beispielsweise Text-Objekten inklusive Schrift, kommen kann. Nach der Eingabe der gewünschten Framegröße wird nun die eigentliche Skalierungsfunktion aufgerufen.

main()
  local
  {
    tDHdl               : handle;   // Dialog
    tSizeWish           : point;    // gewünschte Dialoggröße
  }
{
  // zu skalierender Dialog
  tDHdl # WinOpen('DragDropEvents', _WinOpenDialog | _WinOpenEventsOff);
  if (tDHdl > 0)
  {
    tDHdl->wpAreaClientRestore # false;

    // Wenn die Neuen Icons verwendet werden, dann setzen
    //_App->wpTileTheme # _WinTileThemeEnhanced;

    // Wunschgröße des Dialogs
    tSizeWish # PointMake(1024 , 768);

    // Skalierungsfunktion
    ScaleFrame(tDHdl, tSizeWish:x, tSizeWish:y);

    tDHdl->WinDialogRun(_WinDialogCenterScreen);
    tDHdl->WinClose();
  }
}
Skalierungsfunktion ‘ScaleFrame’:

Dieser Funktion wird der Ausgangsframe, die gewünschte Breite und Höhe übergeben. Zunächst wird die aktuelle Höhe und Breite des Frames und die des Clientbereichs ermittelt. Im Anschluss daran wird nun die Differenz des Clientbereichs des skalierten Dialogs berechnet. Um die Skalierung des Frames durchzuführen, muss der Skalierungsfaktor ermittelt werden, hierfür ist es unerlässlich die prozentuale Abweichung der Höhe und Breite festzustellen. Dies wird durch die Funktion GetDifferencePercent() bewerkstelligt. Ist der Skalierungsfaktor ermittelt worden, so wird der aktuelle Frame auf die gewünschte Größe gesetzt und im Anschluss daran sämtliche Objekte und deren Unterobjekte skaliert.

sub ScaleFrame
(
  aFrame                : handle;       // Frame
  aWidth                : int;          // gewünschte Width
  aHeight               : int;          // gewünschte Height
)

  local
  {
    tWidth              : int;          // Width des Frames
    tHeight             : int;          // Height des Frames
    tClientW            : int;          // Clientbereich Width
    tClientH            : int;          // Clientbereich Height

    tDifW               : int;          // Width Differenz (Dialogrand und Clientbereich)
    tDifH               : int;          // Height Differenz (Dialogrand und Clientbereich)

    tScaleClientW       : int;          // Clientbereich des skalierten Frames (Width)
    tScaleClientH       : int;          // Clientbereich des skalierten Frames (Height)

    tPercentW           : float;        // Width Skalierungsfaktor in %
    tPercentH           : float;        // Height Skalierungsfaktor in %
  }

{
  // Width und Height des Frames
  tWidth  # aFrame->wpAreaRight  - aFrame->wpAreaLeft;
  tHeight # aFrame->wpAreaBottom - aFrame->wpAreaTop;

  // Clientbereich
  tClientW # WinInfo(aFrame, _WinClientWidth);
  tClientH # WinInfo(aFrame, _WinClientHeight);

  // Differenz zwischen Frame- und Clientbereich
  tDifH # tHeight - tClientH;
  tDifW # tWidth - tClientW;

  // Clientbereich des skalierten Frames
  tScaleClientW # aWidth  - tDifW;
  tScaleClientH # aHeight - tDifH;

  // prozentuale Abweichung (Width)
  tPercentW # GetDifferencePercent(tClientW, tScaleClientW);
  // prozentuale Abweichung (Height)
  tPercentH # GetDifferencePercent(tClientH, tScaleClientH);

  // Faktor zum skalieren
  tPercentW  # tPercentW / 100.0;
  tPercentH  # tPercentH / 100.0;

  // Framegröße
  aFrame->wpAreaLeft   # 0;
  aFrame->wpAreaTop    # 0;
  aFrame->wpAreaRight  # aWidth;
  aFrame->wpAreaBottom # aHeight;

  // Objekte und Unterobjekte skalieren
  aFrame->ScaleObject(tPercentW, tPercentH, tClientW, aWidth, aHeight);
}
Funktion ‘ScaleObject’:

Nach der Übergabe des Parent-Objektes, des Skalierungsfaktors der Länge und Breite, des Clientbereichs und der gewünschten Breite und Länge, wird zuerst der Skalierungsfaktor des Fonts ermittelt. Im Anschluss daran erfolgt eine Größenanpassung des Textes der Objekte. Als nächsten Schritt werden nun innerhalb einer Schleife alle Unterobjekte ermittelt und überprüft, ob es sich hierbei nicht um Ausnahmen handelt. Für jedes ermittelete Unterobjekt wird nun die Funktion rekrusiv aufgerufen um ebenfalls eine Größenanpassung des Fonts durchführen zu können. Als finaler Schritt wird am Ende der Funktion jedes einzelne Objekt in seiner Größe um den vorher ermittelten prozentualen Anteil vergrößert.

// Objekte skalieren
sub ScaleObject
(
  aParent               : handle;           // Parent-Objekt
  aPercentW             : float;            // Skalierungsfaktor Width
  aPercentH             : float;            // Skalierungsfaktor Height

  aWidth                : int;              // Clientbereich des alten Frames
  aNewWidth             : int;              // Wunschdialog Width
  aNewHeight            : int;              // Wunschdialog Height
)

  local
  {
    tObj                : handle;           // Objekt

    tArea               : rect;             // Größe

    tGrouping           : int;              // Grouping-Eigenschaft
    tHasGrouping        : logic;            // Grouping gesetzt?
    tHasFont            : logic;            // besitzt ein Font?
    tFontParent         : logic;            // FontParent gesetzt?
    tFont               : font;             // Font des Objekts

    tFontPercent        : float;            // Skalierungsfaktor der FontSize
    tPoint              : float;            // FontSize
  }

{
  // ist Grouping gesetzt?
  if (WinPropGet(aParent, _WinPropGrouping, tGrouping))
    tHasGrouping # tGrouping != _WinGroupingNone;

  // Skalierungsfaktor des Fonts
  tFontPercent # Fnc.CalcPercent(aWidth, aNewWidth, aNewHeight);

  // Text in den Objekten skalieren
  if (WinPropGet(aParent, _WinPropFontParent, tFontParent)) // Eigenschaft vorhanden?
  {
    if (!tFontParent) // false
    {
      tFont  # aParent->wpFont;

      // FontSize liegt in zehntel point vor
      tPoint # CnvFI(tFont:Size) / 10.0;
      // Font skalieren
      tPoint # Fnc.CalcPoint(tPoint, tFontPercent);
      tFont:Size # CnvIF(tPoint) * 10;

      aParent->wpFont # tFont;
    }
  }

  // Objekte suchen
  for   tObj # WinInfo(aParent, _WinFirst);
  loop  tObj # WinInfo(tObj, _WinNext);
  while (tObj > 0)
  {
    // Ausnahmen
    if (WinInfo(tObj, _WinType) = _WinTypeListColumn or
        WinInfo(tObj, _WinType) = _WinTypeGroupColumn or
        WinInfo(tObj, _WinType) = _WinTypeTreeNode or
        WinInfo(tObj, _WinType) = _WinTypeToolbarButton or
        WinInfo(tObj, _WinType) = _WinTypeStatusbar)
      cycle;

    // Unterobjekte suchen
    tObj->ScaleObject(aPercentW, aPercentH, aWidth, aNewWidth, aNewHeight);

    // Ausnahme
    if (WinInfo(tObj, _WinType) = _WinTypeGroupTile)
      cycle;

    // Ausnahme
    if (tHasGrouping)
      if (WinPropGet(tObj, _WinPropAlignGrouping, tGrouping) and tGrouping != _WinAlignGroupingNone)
        cycle;

    // skalieren der Objekte
    tArea # tObj->wpArea;

    tArea:Left   # CnvIF(CnvFI(tArea:Left)   * aPercentW);
    tArea:Top    # CnvIF(CnvFI(tArea:Top)    * aPercentH);
    tArea:Right  # CnvIF(CnvFI(tArea:Right)  * aPercentW);
    tArea:Bottom # CnvIF(CnvFI(tArea:Bottom) * aPercentH);

    tObj->wpArea # tArea;
  }
}
Ausnahmen

Innerhalb von CONZEPT 16 existieren allerdings auch Objekte, welche nicht skaliert werden können. So wird der Spaltenkopf einer Listen-Objektes nicht in seiner Größe angepasst. Auch Checkbox- und Radiobutton-Objekte, sowie Scrollbox-Objekte erfahren keine Skalierung. Gegenwärtig bestehen darüber hinaus noch weitere Punkte, welche vor der Skalierung von Frames beachtet werden sollten, damit ein einwandfreies Ergebnis erreicht wird:

  • die Eigenschaft wpAreaClientRestore darf beim Frame nicht gesetzt werden.
    Nachdem der Frame skaliert und gespeichert wurde kann die Eigenschaft wieder gesetzt werden.
  • Es muss beachtet werden, dass durchgehend die gleiche wpTileTheme Eigenschaft verwendet wird.
  • Sämtliche Objekte sollten einen gewissen Abstand zum Frame-Rand aufweisen
  • Die Eigenschaft wpShowGrip beim Toolbar-Button sollte gesetzt sein
    Es könnte sonst passieren, dass der Toolbar-Button seine bisherige Position ‘verändert’.
  • Der Text der Labels sollte nicht über Rand hinaus gehen.
  • Der Font des RtfEdit-Objektes sollte prozedural nicht geändert werden.

Die im Artikel vorgestellte Prozedur steht am Ende des Artikel als Download bereit.

Download

CONZEPT 16-Prozedur "ScaleDialog" ScaleDialog.zip (1.93 KB)
Sie müssen angemeldet sein, um die Datei herunterladen zu können.

1 Kommentar

1 Kommentar zu “Skalierung von Dialogen”

  1. Wenn der User das Skalieren der Fensterinhalte "on the fly" haben will d.h. wenn er ein Fensterskaliert, kann man das auch in der EvtPosChanged machen. Dann sollte man sich nur die Urpsrungsgrößen der Objekte merken (CteTree) und immer mit diesen rechnen. Skaliert man mit den bereits veränderten Werten, ergeben sich mit jeder neuen Skalierung Rundungsfehler, die das Aussehen dann irgendwann total zerstören.

Kommentar abgeben