Styles in eigene Gui implementieren



  • Hallo, ich schreibe gerade eine GUI in OpenGL und C++ die sich am Aufbau des .Net Framework von MS orientiert. Die ganzen Steuerelemente werden im Moment noch im Code erzeugt, später sollen diese auch aus einer XML-Datei einlesbar sein. Die Frage ist jetzt wie ich verschiedene Styles implementiere. Die Styles sollen sehr primitiv sein, also nur Dinge wie Einschrückungen, Farben, Eckradien usw. beinhalten. Im Moment habe ich für jedes Steuerelement eine eigene Styleklasse, die die selben Design-Eigenschaften wie das Steuerelement enthält.
    Problem: Doppelter Code, sehr Wartungsintensiv bei Änderungen und gefällt mir einfach nicht so wirklich. Gibt es evtl. eine elegantere Lösung?



  • Ich erkenne das Problem nicht so wirklich. Wenn jede Klasse die gleichen Möglichkeiten hat die Eigenschaften zu verändern, so würde doch sowas reichen:

    struct Style
    {
     Color color,
     // ...
    };
    

    Anschließend könntest du feste Styles vordefinieren.

    Irgendwie so (diese Initialisierung wird nicht von allen Compilern unterstützt, glaub ich):

    class Button
    {
    public:
        static Style Standard = {Yellow, ....};
    };
    
    // ...
    
    Button.SetStyle(Button::Standard);
    

    Ansonsten musst du das noch etwas konkretisieren. Zumindest mir erschließt sich das Problem nicht so wirklich...



  • Im Prinzip mache ich es genau so, nur dass meine struct eine class ist. Wenn ich jetzt etwas in der Button-Class ändere muss ich das auch in der ButtonStyle-Class tun. Evtl. auch in der ButtonBase-Class usw. Die einzelnen Variablen habe ich als protected deklariert. D.h. der Nutzer kann auf diese nur mit z.B. Set/GetBackgroundColor zugreifen. In den Funktionen wird dann noch geprüft, ob der Eingabewert gültig ist usw. Ändere ich jetzt eine Bedingung darf ich nicht vergessen das auch in der zugehörigen Style-Klasse zu machen. Aber geht wohl nicht anders.



  • Warum hat Button denn eine setBackgroundColor Methode wenn es dafuer den ButtonStyle gibt?



  • Wenn du es richtig machen willst, schau dir CSS an. Muss ja nicht gleich die komplette Bandbreite sein, die nutzt Qt ja auch nicht, ist aber sehr komfortabel. Vor allem für Neueinsteiger.

    Nimmst eine Style-Klasse, diese bekommt einen Bezeichner der bestimmt für was dieser gilt. Zum Beispiel "Button" für alle Button oder "Button#Horst" für alle Button deren Name Horst ist usw. Dann berechne anhand dieser eine Priorität die du auch in der Klasse speicherst. Sortiere die Liste der geladenen Styles danach.
    Wird nun ein GUI-Object initialisiert gehst du die Liste der geladenen Style-Elemente durch und schaust welches für dieses Objekt gilt. Gehe dabei von niedriger Priorität bis zur höchsten, so überschreibst du auch eventuell nicht mehr relevante Werte mit jenen höherer Priorität.

    Jedes Gui-Object hat dabei eine (abgeleitete) applyStyle-Funktion oder sowas, die sich die nötigen Informationen rausholt. Hierbei hilft es natürlich (und bei vielen anderen Dingen) wenn man abstrakte Überklassen hat, die die Gui-Elemente strukturieren in ihrer Hierarchie (AbstractButton zB für PushButtons, Checkboxes und RadioButtons etc.).



  • Shade Of Mine schrieb:

    Warum hat Button denn eine setBackgroundColor Methode wenn es dafuer den ButtonStyle gibt?

    Es sollen ja auch mehrere Styles verwendet werden können, bzw. die einzelnen Eigenschaften des Button auch individuell angepasst werden können ohne dafür einen neuen Style entwerfen zu müssen.

    @Fellhuhn
    Ja oder eben X(A)ML anstatt CSS.



  • Student83 schrieb:

    Es sollen ja auch mehrere Styles verwendet werden können, bzw. die einzelnen Eigenschaften des Button auch individuell angepasst werden können ohne dafür einen neuen Style entwerfen zu müssen.

    Das beantwortet die Frage nicht. Denn das kannst du auch alles ueber die Style Klasse machen. Richtig flexibel ist es natuerlich wenn es einen computed-style gibt wie Fellhuhn beschreibt.

    Aber der Punkt ist, dass du eine Style Klasse hast die sich um Styles kuemmert. Und eine Control Klasse die sich um das Control kuemmert. Dann hast du naemlich auf einen Schlag alle deine Probleme aus deinem ersten Post geloest.

    Ein Button kann ja durchaus auch ein Style-Objekt als Member haben.



  • Wenn ich das also richtig verstanden habe soll ich jeder GUI-Element-Klasse ein Style-Member hinzufügen. Allerdings bräuchte ich dann ja so eine Art Super-Style der die Style-Eigenschaften aller Controls enthält. Damit würde aber, da die unterschiedlichen Styles im Umfang sehr unterschiedlich sind, bei den primitiveren GUI-Element-Klassen viele unnütze Variablen zur Verfügung gestellt und damit viel Speicher verschwendet. Oder habt ihr das anders gemeint?



  • class Style
    {
    public:
      Color ButtonColor();
      Color TextColor();
      Font FontStyle();
      // ....
    };
    
    class Button
    {
    public:
      /*static ?*/void ChangeStyle(const Style& s)
      {
        color_ = s.ButtonColor();
      }
    private:
      /*static ?*/ Color color_;
    };
    


  • Ok, allerdings muss ich die Style-Variablen als private setzen (wegen Gültigkeitsprüfung), damit auf diese nur über Set/Get-Funktionen zugegriffen werden kann.
    Außerdem muss ich die ganzen Variablen so in der Button-Klasse und der Style-Klasse angeben. Wäre es nicht eleganter die Buttonklasse von der Style Klasse abzuleiten:

    class Button : public Style {...};
    

    So hätte ich den Style komplett von der Buttonklasse getrennt. Allerdings kommt es dann ja zum Diamant-Ploblem.

    Nehmen wir mal folgendes Beispiel:

    class Style {...};
    class ControlStyle : public Style {...};
    class ButtonStyle : public ControlStyle {...};
    

    und

    class Control : public ControlStyle {...};
    class Button : public Control, ButtonStyle {...};
    

    Die Klasse Button bekommt nun den ControlStyle und den ButtonStyle vererbt, was so nicht sein soll.



  • Ich verstehe dein Problem nicht, aber Button von Style erben zu lassen ist absoluter Blödsinn. Button ist ein Fesnter oder ein Widget oder was auch immer, aber Button ist kein Style. Es hat einen Style oder benutzt einen Style um sich selber zu zeichnen.



  • Wenn ich den Style als public Member einer Steuerlement-Klasse deklariere kann ich z.B. so:

    m_pControl->Style->SetWidth(50);
    

    darauf zugreifen.
    Ich muss allerdings den Style als private deklarieren da ja sonst der z.B. Control-Style an alle abgeleitete Klassen vererbt werden würde. Jetzt kann allerdings der Style auch nicht mehr von außen verändert werden. Folgendes geht also nicht mehr:

    Button *m_pButton;
    m_pButton = new Button();
    m_pButton->Style->SetWidth(60);
    m_pButton->Style->SetHeight(20);
    

    Also brauche ich für jede Steuerelement-Klasse nun auch noch Set/Get-Funktionen um jede Variable des Styles verändern zu können. Und genau diesen zusätzlichen Wülst an Funktionen möchte ich mir sparen. Darum möchte ich wissen ob es nicht ein besseres Konzept gibt.



  • Student83 schrieb:

    Also brauche ich für jede Steuerelement-Klasse nun auch noch Set/Get-Funktionen um jede Variable des Styles verändern zu können. Und genau diesen zusätzlichen Wülst an Funktionen möchte ich mir sparen. Darum möchte ich wissen ob es nicht ein besseres Konzept gibt.

    Warum nicht ein Get/Set-Paar für den Style?

    class Button
    {
        public:
            Style GetStyle() const;
            void  SetStyle(const Style& style);
    };
    


  • @Nexus
    Das wäre natürlich schonmal viel praktischer. Wie sieht das denn von der Ausführungdsgeschwindigkeit aus wenn ich z.B. nur die Breite eines Button zurückgegeben haben möchte und

    button->GetStyle()->GetWidth();
    

    aufrufe. Wird durch diesen Aufruf wirklich nur die Breite zurückgegeben. Oder im erten Schritt sozusagen zunächst der ganze Style und im zweiten Schritt dann nur noch die Breite?

    Und kann mir evtl. noch jemand erklären wie die Implementation von den Styles bei Qt gelöst ist?

    Dort scheint der Style über einen Zeiger realisiert zu sein:

    public:
        QStyle *style() const;
        void setStyle(QStyle *);
    

    Achso, und was bewirkt eigentlich "() const" hinter einer Variablen?



  • Student83 schrieb:

    Achso, und was bewirkt eigentlich "() const" hinter einer Variablen?

    autsch



  • Autsch, wenn deine fast 1000 anderen Beiträge auch so aussehen 🙄. Erklären kannst du es offensichtlich nicht. Ich programmiere schon lange und das habe ich jetzt zum ersten mal bei Qt gesehen.
    Hab jetzt selbst die Erklärung gefunden:
    http://en.wikipedia.org/wiki/Const-correctness



  • sry, aber diese frage tat wirklich weh.

    Achso, und was bewirkt eigentlich "() const" hinter einer Variablen?

    '()' bewirkt, das es sich um eine memberfunktion handelt und nicht um eine variable. const hast du ja scheinbar inzwischen schon rausgefunden.

    das autsch bezog sich nicht auf das const



  • Student83 schrieb:

    Ich programmiere schon lange und das habe ich jetzt zum ersten mal bei Qt gesehen.

    Nicht böse gemeint, aber programmierst du auch schon lange C++? Sowohl const als auch Memberfunktionen sind etwas ziemlich Grundlegendes und vor allem auch etwas, das einem andauernd im C++-Alltag begegnet.

    Wegen Performance: Du kannst ja Pass by Reference benutzen, wenn es angebracht ist. Ein bisschen kopieren musst du halt je nachdem immer noch. Aber wenn du den Benutzer nicht direkt das Style -Objekt innerhalb Button ändern lässt, muss er halt jeweils einen neuen Style erstellen und den setzen.



  • @ Meep Meep
    Jetzt sehe ich es auch, ich habe da wohl etwas überzogen reagiert. Das const hat mich so irritiert dass ich nicht erkannt habe, dass das ne Funktion und keine Varible ist. Da kann man wirklich nur autsch schreiben ;). Hätten die die Funktion "getStyle" genannt wäre das auch sofort klar gewesen.


Log in to reply