Entwicklung - Eigenständiges Skin Plugin erstellen
(→Einleitung) |
Wirbel (Diskussion | Beiträge) K (Änderungen von 89.233.145.209 (Diskussion) rückgängig gemacht und letzte Version von 87.173.160.250 wiederhergestellt) |
||
Zeile 2: | Zeile 2: | ||
− | + | ==Einleitung== | |
+ | Ich möchte hier beschreiben wie man ein Skin-Plugin erstellt. Dabei ist nicht das Erstellen einer XML basierenden Beschreibung für das Text2Skin Plugin gemeint. | ||
+ | Hier wird vielmehr beschrieben, wie man ein eigenes Plugin zur Darstellung für Skins schreibt. | ||
==Vor- und Nachteile== | ==Vor- und Nachteile== |
Version vom 9. November 2009, 11:41 Uhr
HIER GIBT ES NOCH EIN PAAR SACHEN ZUM AUSFÜLLEN. BITTE FÜLLE ES AUS WENN DU ES KANNST
Inhaltsverzeichnis |
Einleitung
Ich möchte hier beschreiben wie man ein Skin-Plugin erstellt. Dabei ist nicht das Erstellen einer XML basierenden Beschreibung für das Text2Skin Plugin gemeint. Hier wird vielmehr beschrieben, wie man ein eigenes Plugin zur Darstellung für Skins schreibt.
Vor- und Nachteile
Vorteile von einem Skin Plugin gegenüber dem Text2Skin Plugin:
- Es gibt einem mehr Möglichkeiten
- Totale Kontrolle über das Plugin
- etwas performanter als die xml Beschreibung
Nachteile:
- viel mehr Aufwand
- Fehleranfälliger, da viel mehr Code selber geschrieben werden muss
Grundgerüst
Als erstes sollte man sich mit dem Skript "newplugin" ein neues Grundgerüst erstellen lassen.
./newplugin myskin
Somit erhält man folgenden Code. Ich habe es hier nur auf die wirklich nötigen Methoden reduziert:
/* * myskin.c: A plugin for the Video Disk Recorder * * See the README file for copyright information and how to reach the author. * * $Id$ */ #include <vdr/plugin.h> static const char *VERSION = "0.0.1"; static const char *DESCRIPTION = "Enter description for 'myskin' plugin"; static const char *MAINMENUENTRY = "Myskin"; class cPluginMyskin : public cPlugin { private: // Add any member variables or functions you may need here. public: cPluginMyskin(void); virtual ~cPluginMyskin(); virtual const char *Version(void) { return VERSION; } virtual const char *Description(void) { return DESCRIPTION; } virtual bool Start(void); virtual const char *MainMenuEntry(void) { return MAINMENUENTRY; } virtual cString SVDRPCommand(const char *Command, const char *Option, int &ReplyCode); }; cPluginMyskin::cPluginMyskin(void) {} cPluginMyskin::~cPluginMyskin() {} bool cPluginMyskin::Start(void) { new cSkinMySkin; return true; } VDRPLUGINCREATOR(cPluginMyskin); // Don't touch this!
Die Zeile
new cSkinMySkin muss ergänzt werden.
Somit haben wir das Grundgerüst um ein Plugin zu erstellen.
Basis Skin Klasse
Kommen wir nun zu der Basis Skin Klasse die wir zuvor im Plugin-Grundgerüst eingefügt haben. Die Klasse sollte in etwas so aussehen:
class cSkinMySkin : public cSkin { public: cSkinMySkin(void); virtual const char *Description(void); virtual cSkinDisplayMenu *DisplayMenu(void); virtual cSkinDisplayChannel *DisplayChannel(bool WithInfo); virtual cSkinDisplayReplay *DisplayReplay(bool ModeOnly); virtual cSkinDisplayVolume *DisplayVolume(void); virtual cSkinDisplayTracks *DisplayTracks(const char *Title, int NumTracks, const char * const *Tracks); virtual cSkinDisplayMessage *DisplayMessage(void); };
Wir sehen das hier wieder eine vielzahl von Klassen benötigt werden. Um zu verstehen, was welche Klasse macht, müssen wir erstmal ein paar Sachen definieren.
Was macht welche Klasse?
Es ist erstmal wichtig zu verstehen was wo gemacht wird. Dazu will ich zeigen was es alles für Ansichten in VDR gibt.
Implementierung der Skin Basis Klasse
Die Implementierung unserer Skin Basisklasse ist recht einfach, sie gibt nur die speziellen Klassen wieder:
cSkinMySkin::cSkinMySkin(void):cSkin("myskin", &::Theme) {} const char *cSkinMySkin::Description(void) { return tr("My first Skin"); } cSkinDisplayChannel *cSkinMySkin::DisplayChannel(bool WithInfo) { return new cSkinMySkinDisplayChannel(WithInfo); } cSkinDisplayMenu *cSkinMySkin::DisplayMenu(void) { return new cSkinMySkinDisplayMenu; } cSkinDisplayReplay *cSkinMySkin::DisplayReplay(bool ModeOnly) { return new cSkinMySkinDisplayReplay(ModeOnly); } cSkinDisplayVolume *cSkinMySkin::DisplayVolume(void) { return new cSkinMySkinDisplayVolume; } cSkinDisplayTracks *cSkinMySkin::DisplayTracks(const char *Title, int NumTracks, const char * const *Tracks) { return new cSkinMySkinDisplayTracks(Title, NumTracks, Tracks); } cSkinDisplayMessage *cSkinMySkin::DisplayMessage(void) { return new cSkinMySkinDisplayMessage; }
Skin Klassen
Nun kommen wir zu den Speziellen Klassen:
SkinDisplayMenu
Beginnen wir mit dem Menü:
class cSkinMySkinDisplayMenu : public cSkinDisplayMenu { private: .... public: cSkinMySkinDisplayMenu(void); virtual ~cSkinMySkinDisplayMenu(); virtual void Scroll(bool Up, bool Page); virtual int MaxItems(void); virtual void Clear(void); virtual void SetTitle(const char *Title); virtual void SetButtons(const char *Red, const char *Green = NULL, const char *Yellow = NULL, const char *Blue = NULL); virtual void SetMessage(eMessageType Type, const char *Text); virtual void SetItem(const char *Text, int Index, bool Current, bool Selectable); virtual void SetEvent(const cEvent *Event); virtual void SetRecording(const cRecording *Recording); virtual void SetText(const char *Text, bool FixedFont); virtual void Flush(void); };
Hier eine kurze Skizze, was was macht:
/* * Base Background will be drawn in the Constructor * * x0 * y0------------------------------------------------------ * | MySkin VDR - Main Menu | <-----DrawTitle() * | 02.03.06 - 17:23 | <---included in Flush() * y1------------------------------------------------------ * | | \ * | *Schedule <-|-------DrawItem() \ * | *Channels | \ * | *Timers | \ * | *Recordings | \ * | *Plugin 1 | \Clear()/SetEvent()/SetRecordings()/SetText() * | *Plugin 2 | / * | | / * | | / * | | / * | | / * | | / *y2|---------------\ | * | Any Logo \ (Channel not avalible) <----|-----------SetMessage() * | \ | *y21------------------------------------------------------ * | | * | (RED) (GREEN) (YELLOW) (BLUE) | <------SetButtons() * | | *y3------------------------------------------------------ */
Im Constructor werden Variablen initialisiert, die Zeichenfläche besorgt und statische Elemente gezeichnet.
cSkinMySkinDisplayMenu::cSkinMySkinDisplayMenu(void) { //guter Zeitpunkt um ein paar Schriften zu definieren const cFont *font = cFont::GetFont(fontOsd); lineHeight = font->Height(); //Es ist immer Sinnvoll ein paar Abstände und Punkte im Menü zu definieren x0 = 0; ..... x7 = ...; y0 = 0; ..... y7 = ...; //hier soll der Speicher für die Fläche auf der Gezeichnet wird reserviert werden. //weitere Infos dazu sind in der PLUGIN.html Datei von Klaus osd = cOsdProvider::NewOsd(Setup.OSDLeft, Setup.OSDTop); tArea Areas[] = { { x0, y0, x7 - 1, y7 - 1, 4 } }; if (osd->CanHandleAreas(Areas, sizeof(Areas) / sizeof(tArea)) == oeOk) osd->SetAreas(Areas, sizeof(Areas) / sizeof(tArea)); else { tArea Areas[] = { { x0, y0, x7 - 1, y3 - 1, 2 }, { x0, y3, x3 - 1, y4 - 1, 1 }, { x3, y3, x4 - 1, y4 - 1, 2 }, { x4, y3, x7 - 1, y4 - 1, 2 }, { x0, y4, x7 - 1, y7 - 1, 4 } }; osd->SetAreas(Areas, sizeof(Areas) / sizeof(tArea)); } //Statische Elemente sollten hier gezeichnet werden. Dazu gehören Hintergründe, Header und Fooder osd->DrawRectangle(x0, y0, x7 - 1, y7 - 1, Theme.Color(clrBackground)); ....... osd->DrawEllipse (x6, y6, x7 - 1, y7 - 1, frameColor, 5); }
Freigeben des OSDs
cSkinMySkinDisplayMenu::~cSkinMySkinDisplayMenu() { delete osd; }
Wenn eine Tabelle oder ein Text im Menü über mehr als nur eine Seite geht, hat man hier die Möglichkeit, Pfeile an Rand zu zeichnen. Der Aufruf sollte an die Parent Klasse weiter gereicht werden.
void cSkinMySkinDisplayMenu::Scroll(bool Up, bool Page) { //Reicht den Skroll Aufruf weiter cSkinDisplayMenu::Scroll(Up, Page); //Hier sollten Pfeile gezeichnet werden wenn es was zum Scrollen gibt }
In Tabellen muss VDR wissen wie viele Einträge(Items) in eine Seite passen. Diese Methode wird nur einmal beim initialisieren aufgerufen.
int cSkinMyskinDisplayMenu::MaxItems(void) { return (int)(y2 - y1) / lineHeight; }
Nicht statische Elemente können hier wieder gelöscht werden. Wird aufgerufen, wenn man von einem Menü zu einem anderen kommt. Man sollte nur den Bereich überschreiben, der nicht vom Constructor gezeichnet wurde.
void cSkinMyskinDisplayMenu::Clear(void) { //hier wird z.B. das mittlere Feld mit der Hintergrundfarbe wieder überschieben. osd->DrawRectangle(x1, y3, x7 - 1, y4 - 1, Theme.Color(clrBackground)); }
In dem Header können unterschiedliche Informationen stehen. Neben dem Titel(name des Menüs) können noch weitere Informationen in den Header gepackt werden. Beliegt ist hier z.B. die Uhrzeit. Hier sollte jedoch nicht der Hintergrund des Headers gezeichnet werden. Dieses fällt in dem Bereich statische Elemente die vom Constructor gezeichnet werden.
void cSkinMyskinDisplayMenu::SetTitle(const char *Title) { const cFont *font = cFont::GetFont(fontOsd); const char *VDR = " VDR"; int w = font->Width(VDR); osd->DrawText(x3 + 5, y0, Title, Theme.Color(clrMenuTitle), frameColor, font, x4 - w - x3 - 5); osd->DrawText(x4 - w, y0, VDR, frameColor, clrBlack, font); }
Hier können die 4 Farbiegen Buttons am unteren Rand gezeicht werden. Ist der Text für einen Button leer, so muss er z.B. nicht gezeichnet werden.
void cSkinMyskinDisplayMenu::SetButtons(const char *Red, const char *Green, const char *Yellow, const char *Blue) { }
Die Methode SetMessage ist in jeder SkinKlasse vorhanden, da sie die Möglichkeit gibt, Nachrichten an den User zu schicken, egal, in was für einem OSD er gerade ist. Der Programmierer hat damit die Möglichkeit die Nachricht an sein Menü anzupassen. Der Text ist meist sehr kurz und immer einzeilig. Der Type gibt wieder um was für eine Nachricht es sich handelt.
Beispiele:
"Kannal nicht verfügbar"
"Wollen Sie die Aufzeichnung wirklich löschen?"
void cSkinMyskinDisplayMenu::SetMessage(eMessageType Type, const char *Text) { }
Wohl eine der umfangreichsten Methoden ist die SetItem. Diese Methode gibt an wie ein Listeneintrag aus sehen soll.
void cSkinMyskinDisplayMenu::SetItem(const char *Text, int Index, bool Current, bool Selectable) { if (Current) { //Aktuell ausgewählter Eintrag } else { if(Selectable) //Eintrag könnte man auswählen else //Eintrag kann man nicht auswählen } //Text kann über Tabs formatiert werden. for (int i = 0; i < MaxTabs; i++) { const char *s = GetTabbedText(Text, i); if (s) { int xt = x3 + 5 + Tab(i); //Schreibt den Text an die errechnete Position osd->DrawText(xt, y, s, ColorFg, ColorBg, font, x4 - xt); } if (!Tab(i + 1)) break; } SetEditableWidth(x4 - x3 - 5 - Tab(1)); }
Ab hier kommen ein paar Methoden die Sonderfunktionen haben.
Hier werden die EPG Daten auf dem OSD angezeigt.
Menu->Schedule-> [select one] Enter
void cSkinMyskinDisplayMenu::SetEvent(const cEvent *Event) { }
Es werden Informationen über eine Aufzeichnung über den gesammten inneren Bereich angezeigt.
Menu->Recordings-> [select one] BlueButton
void cSkinMyskinDisplayMenu::SetRecording(const cRecording *Recording) { }
Wenn ein langer Text im inneren Bereich angezeigt werden soll, dann wird dieses über diese Methode gemacht. Leider fällt mir dabei kein VDR eigenes OSD ein, wo es zum Einsatz kommt. Es wird z.B. vom Manuell Plugin verwendet.
Der Text wird automatisch umgebrochen.
void cSkinMyskinDisplayMenu::SetText(const char *Text, bool FixedFont) { }
Wie das Flush von printf. Zeichnet die Buffer "Sofort" ins OSD. Hier sind auch Sachen gut aufgehoben, die bei gezeichneten OSD nachträglich hin und wieder geändert werden sollen. Ein Beispiel hierfür ist die Uhr.
void cSkinMyskinDisplayMenu::Flush(void) { }
SkinDisplayReplay
class cSkinMySkinDisplayReplay : public cSkinDisplayReplay { private: public: cSkinMySkinDisplayReplay(bool ModeOnly); virtual ~cSkinMySkinDisplayReplay(); virtual void SetTitle(const char *Title); virtual void SetMode(bool Play, bool Forward, int Speed); virtual void SetProgress(int Current, int Total); virtual void SetCurrent(const char *Current); virtual void SetTotal(const char *Total); virtual void SetJump(const char *Jump); virtual void SetMessage(eMessageType Type, const char *Text); virtual void Flush(void); };
Hier gelten die selben Regeln wie für das Menü
cSkinMySkinDisplayReplay::cSkinMySkinDisplayReplay(bool ModeOnly) { }
Freigeben des OSDs
cSkinMySkinDisplayReplay::~cSkinMySkinDisplayReplay() { delete osd; }
Hier gelten die selben Regeln wie für das Menü
void cSkinMySkinDisplayReplay::SetTitle(const char *Title) { }
Abhängig davon wie die Aufnahme gerade wiedergegeben wird, können hier verschiedene Informationen angezeigt werden.
void cSkinMySkinDisplayReplay::SetMode(bool Play, bool Forward, int Speed) { if (Speed < -1) Speed = -1; if (Speed > 3) Speed = 3; cBitmap bm(ReplaySymbols[Play][Forward][Speed + 1]); osd->DrawBitmap(x0 + (x1 - x0 - bm.Width()) / 2, y3 + (y4 - y3 - bm.Height()) / 2, bm, Theme.Color(clrReplayMode), frameColor); }
Hier wird der Fortschrittsbalken angezeigt. Die Parameter geben an, wie weit die Wiedergabe gerade ist und wie lang die Aufzeichnung ist.
void cSkinMySkinDisplayReplay::SetProgress(int Current, int Total) { cProgressBar pb(x4 - x3, y4 - y3, Current, Total, marks, Theme.Color(clrReplayProgressSeen), Theme.Color(clrReplayProgressRest), Theme.Color(clrReplayProgressSelected), Theme.Color(clrReplayProgressMark), Theme.Color(clrReplayProgressCurrent)); osd->DrawBitmap(x3, y3, pb); }
Die beiden Methoden SetCurrent und SetTotal geben in Textform wieder, wie weit die Aufzeichnung gerade ist.
void cSkinMySkinDisplayReplay::SetTotal(const char *Total) { } void cSkinMySkinDisplayReplay::SetCurrent(const char *Current) { }
Hier muss ich selber gerade Passen.....
void cSkinMySkinDisplayReplay::SetJump(const char *Jump) { osd->DrawText(x0 + (x4 - x0) / 4, y6, Jump, Theme.Color(clrReplayJump), frameColor, cFont::GetFont(fontSml), (x4 - x3) / 2, 0, taCenter); }
Wie bereits beim Menü geschrieben hat jede Ansicht eine SetMessage, somit kann es immer an das aktuelle OSD angepasst werden.
void cSkinMySkinDisplayReplay::SetMessage(eMessageType Type, const char *Text) { }
Das selbe wie beim Menü
void cSkinMySkinDisplayReplay::Flush(void) { osd->Flush(); }
SkinDisplayVolume
class cSkinMySkinDisplayVolume : public cSkinDisplayVolume { private: public: cSkinMySkinDisplayVolume(void); virtual ~cSkinMySkinDisplayVolume(); virtual void SetVolume(int Current, int Total, bool Mute); virtual void Flush(void); };
Die selben Regeln wie beim Menü
cSkinMySkinDisplayVolume::cSkinMySkinDisplayVolume(void) { }
Freigabe des OSDs
cSkinMySkinDisplayVolume::~cSkinMySkinDisplayVolume() { delete osd; }
Zeichnet den Lautstärke Balken ins OSD
void cSkinMySkinDisplayVolume::SetVolume(int Current, int Total, bool Mute) { }
Selbe wie beim Menü
void cSkinMySkinDisplayVolume::Flush(void)
{ osd->Flush(); }
DisplayTracks
Bitte ausfüllen (bin mir gerade nicht sicher wann das angezeigt wird)
class cSkinMySkinDisplayTracks : public cSkinDisplayTracks { private: public: cSkinMySkinDisplayTracks(const char *Title, int NumTracks, const char * const *Tracks); virtual ~cSkinMySkinDisplayTracks(); virtual void SetTrack(int Index, const char * const *Tracks); virtual void SetAudioChannel(int AudioChannel); virtual void Flush(void); };
Bitte ausfüllen
cSkinMySkinDisplayTracks::cSkinMySkinDisplayTracks(const char *Title, int NumTracks, const char * const *Tracks) { }
Gibt das OSD wieder frei
cSkinMySkinDisplayTracks::~cSkinMySkinDisplayTracks() { delete osd; }
Bitte ausfüllen
void cSkinMySkinDisplayTracks::SetItem(const char *Text, int Index, bool Current) { }
Bitte ausfüllen
void cSkinMySkinDisplayTracks::SetTrack(int Index, const char * const *Tracks) { }
Biite ausfüllen
void cSkinMySkinDisplayTracks::SetAudioChannel(int AudioChannel) { }
Wie beim Menü
void cSkinMySkinDisplayTracks::Flush(void) { osd->Flush(); }
DisplayMessage
Wenn kein OSD offen ist, dann werden die Messages über diese Methode angezeigt.
class cSkinMySkinDisplayMessage : public cSkinDisplayMessage { private: public: cSkinMySkinDisplayMessage(void); virtual ~cSkinMySkinDisplayMessage(); virtual void SetMessage(eMessageType Type, const char *Text); virtual void Flush(void); };
Selben Regeln wie beim Menü
cSkinMySkinDisplayMessage::cSkinMySkinDisplayMessage(void) { }
Gibt den OSD wieder frei
cSkinMySkinDisplayMessage::~cSkinMySkinDisplayMessage() { delete osd; }
Selbe wie beim Menü
void cSkinMySkinDisplayMessage::SetMessage(eMessageType Type, const char *Text) { }
Selbe wie beim Menü
void cSkinMySkinDisplayMessage::Flush(void) { osd->Flush(); }