Tworzenie skórek do programu
Wolverine
Dynamiczne tworzenie interfejsu
Ten artykuł napisałem raczej dla mniej zaawansowanych użytkowników Delphi, którzy bez żadnych drogich komponentów pragną wbudować w swoja aplikacje obsługę skórek.
Mój pomysł różni się tym od innych, że nie opiera się na zmianie właściwości jakiegoś elementu tylko sam te elementy tworzy.
Zacznę od przedstawienia formatu pliku, który utworzy poszczególne elementy interfejsu.
KOD: |
//Hyper Objects Language version 1.0.0
hol;version=100;Author=Wolverine
|
</tr></table>
Funkcja jak można zauważyć dzieli stringa na TStringList wg danego znaku. Będzie nam potrzebna jeszcze jedna funkcja, tworząca ścieżkę do katalogu ze skinami:
DELPHI:</td></tr>
```delphi
function GetPath(Path: String): String;
begin
Result := Path;
if Pos('%skinroot%', Path) < 0 then
Result := ExtractFilePath(Application.ExeName) + '\skins' + Copy(Path, 11, Length(Path) - 10);
end;
```
</td></tr></table>
Teraz główna część programu – kod przetwarzający plik HOL:
DELPHI:</td></tr>
```delphi
procedure TMainForm.LoadSkin(FileName: String);
var
Code: TStringList;
q, w, te: Integer;
TempString, Temp: String;
//kontrolki
Image: TImage;
CoolForm: TCoolForm;
Text: TLabel;
begin
Code := TStringList.Create; //Caly kod
MainFormElements := TStringList.Create;
Form2Script := TStringList.Create; //Skrypt zawierajacy kod interfejsu głównej formy (zmienna globalna)
MainFormScript := TStringList.Create; //Skrypt zawierajacy kod interfejsu form2 (zmienna globalna)
Code.LoadFromFile(FileName);
for q := 0 to Code.Count -1 do begin //Ta pentla rozdzieli elementy nalerzące do MainForm i Form2
if LowerCase(Code[q]) = 'mainform;begin' then te := 1;
if LowerCase(Code[q]) = 'form2;begin' then te := 2;
if te = 1 then MainFormScript.Add(Code[q]);
if te = 2 then Form2Script.Add(Code[q]);
end;
for q := 0 to MainFormScript.Count -1 do begin
if Pos('//', MainFormScript[q]) <> 0 then Continue; //Jeśli to komentarz, przejdź do następnej linijki
if Length(MainFormScript[q]) = 0 then Continue; //Podobnie jeśli linia jest pusta
MainFormScript[q] := LowerCase(MainFormScript[q]);
//Image
if ParseCmd(MainFormScript[q], ';')[0] = 'image' then begin
Image := TImage.Create(MainForm); //Tworzymy komponent
Image.Parent := MainForm;
Image.AutoSize := True;
for w := 1 to ParseCmd(MainFormScript[q], ';').Count -1 do begin //I nadajemy wlaściwości
Temp := ParseCmd(MainFormScript[q], ';')[w];
if ParseCmd(Temp, '=')[0] = 'top' then
Image.Top := StrToInt(ParseCmd(Temp, '=')[1]);
if ParseCmd(Temp, '=')[0] = 'left' then
Image.Left := StrToInt(ParseCmd(Temp, '=')[1]);
if ParseCmd(Temp, '=')[0] = 'image' then
Image.Picture.LoadFromFile(GetPath(ParseCmd(Temp, '=')[1]));
if ParseCmd(Temp, '=')[0] = 'function' then
TempString := ParseCmd(Temp, '=')[1];
end;
if TempString = 'mainmenu' then //Jesli obiekt ma jakąś funkcje to przypisz odpowiednie zdarzenia
Image.OnClick := MainMenuClick;
if TempSTring = 'statusmenu' then
Image.OnClick := StatusMenuClick;
if TempSTring = 'hide' then
Image.OnClick := HideClick;
MainFormElements.AddObject(TempString, Image); //Robimy liste elementów, aby móc później je zniszczyć
Continue; //Następna linijka
end;
//Region
if ParseCmd(MainFormScript[q], ';')[0] = 'region' then begin
CoolForm := TCoolForm.Create(MainForm);
CoolForm.Parent := MainForm;
CoolFOrm.Align := alClient;
for w := 1 to ParseCmd(MainFormScript[q], ';').Count -1 do begin
Temp := ParseCmd(MainFormScript[q], ';')[w];
if ParseCmd(Temp, '=')[0] = 'top' then
CoolForm.Top := StrToInt(ParseCmd(Temp, '=')[1]);
if ParseCmd(Temp, '=')[0] = 'left' then
CoolForm.Left := StrToInt(ParseCmd(Temp, '=')[1]);
if ParseCmd(Temp, '=')[0] = 'image' then
CoolForm.Picture.LoadFromFile(GetPath(ParseCmd(Temp, '=')[1]));
if ParseCmd(Temp, '=')[0] = 'mask' then
CoolForm.LoadMaskFromFile(GetPath(ParseCmd(Temp, '=')[1]));
end;
MainFormElements.AddObject('None', CoolForm);
Continue;
end;
//Label
if ParseCmd(MainFormScript[q], ';')[0] = 'label' then begin
Text := TLabel.Create(MainForm);
Text.Parent := MainForm;
Text.Transparent := True;
Text.OnMouseDown := FormCaptionMouseDown;
for w := 1 to ParseCmd(MainFormScript[q], ';').Count -1 do begin
Temp := ParseCmd(MainFormScript[q], ';')[w];
if ParseCmd(Temp, '=')[0] = 'top' then
Text.Top := StrToInt(ParseCmd(Temp, '=')[1]);
if ParseCmd(Temp, '=')[0] = 'left' then
Text.Left := StrToInt(ParseCmd(Temp, '=')[1]);
if ParseCmd(Temp, '=')[0] = 'fontcolor' then
Text.Font.Color := StringToColor(ParseCmd(Temp, '=')[1]);
if ParseCmd(Temp, '=')[0] = 'fontname' then
Text.Font.Name := ParseCmd(Temp, '=')[1];
if ParseCmd(Temp, '=')[0] = 'fontsize' then
Text.Font.Size := StrToInt(ParseCmd(Temp, '=')[1]);
if ParseCmd(Temp, '=')[0] = 'b' then
Text.Font.Style := Text.Font.Style + [fsBold];
if ParseCmd(Temp, '=')[0] = 'i' then
Text.Font.Style := Text.Font.Style + [fsItalic];
if ParseCmd(Temp, '=')[0] = 'u' then
Text.Font.Style := Text.Font.Style + [fsUnderline];
if ParseCmd(Temp, '=')[0] = 'function' then
TempString := ParseCmd(Temp, '=')[1];
end;
MainFormElements.AddObject(TempString, Text); //Tutaj nasz TstringList ma jeszcze większe znaczenie - patrz dalej
Continue;
end;
end;
end;
</td></tr></table>
W Form2 sprawa ma się analogicznie jak w przypadku MainForm, z tym, że kod jest przechowywany w Form2Stript i odciąć należy pętle dzielącą kod na dwie formy
A teraz obiecane przedstawienie znaczenia MainFormElements. DO StringLista dodaliśmy element i jego funkcje. Label z założenia powyższego kodu ma funkcje jedynie informacyjna (nie można na niego klikać), i wlaśnie do tego potrzebujemy nazwe jego funkcji:
<table border="0px" bgcolor="#EEEEEE"><tr><td bgcolor="#DDDDDD"><b>DELPHI:</b></td></tr><tr>
<td>
```delphi
procedure TMainForm.SetCaption(Caption: String);
var
q: Integer;
begin
for q := 0 to MainFormElements.Count -1 do
if MainFormElements[q] = 'caption' then
(MainFormElements.Objects[q] as TLabel).Caption := Caption;
end;
</td></tr></table>
Procedura szuka Labela, który spełnia funkcje Caption i ustawia w nim odpowiednią właściwość.
Język może również posłużyć innym celom takim jak np. przeglądarka internetowa, więc mam nadzieję, że wykorzystacie go jak tylko się da. Powodzenia w pisaniu! Z powodu braku czasu nie napisałem żadnego źródła, które wystarczyłoby skompilować, ale postaram się to jak najszybciej nadrobić.
W TMainForm.ParseCmd tworzony jest obiekt TStringList:
Result := TStringList.Create; //Wyniki beda trzymane w TStringList
a gdzie jest zwalniany?
W programie są odwołania do tej funkcji np:
if ParseCmd(Temp, '=')[0] = 'fontsize' then
Mnie to pachnie wyciekiem pamięci. Jeśli się mylę to proszę o wytłumaczenie.
ja to bym na bitmapach zrobił...
Mam pytanie na śniadanie:
Czy tak sworzony obiekt po tym jak się go przykryje innym oknem a potem odkryje to on się pojawia piękny i kolorowy, czy też może jest lekko wyczyszczony w miejscu gdzie był przykryty ?
art jest ok. Może można by jednak troszkę na strumieniach pooperować? tyż można, tylko plików będzie więcej
nie przesadzasz troche z... ???!!! :-)
nie przesadzasz troche z... ???!!! :-)
Jak tera patrze na ten kod, to wydaje mi sie beznadzijny :), lepiej korzystac z THolFile, w arcie go nie ma, bo powstal po jego napisaniu. Zapraszam do downloadu ;)
Heh... Duzo, gratuluje arta, bardzo fajny, chociaz trochę nierozumiem(z kodu)... LOL
[url]http://4programmers.net/view_file.php?id=1506[/url] - klasa ulatwiajaca uzywanie jezyka HOL
GOOD!! PRZYDA SIĘ!!
[quote]Na C to daj to bedzie ok.[/quote] dal bym, ale na c sie kompletnie nie znam :/
teraz to samo w C++
CoolForm.LoadMaskFromFile(GetPath(ParseCmd(Temp, '=')[1])); - wczytuje maske z pliku, ale thx za sposob, ktory sam tworzy maske
Żeby forma miałą nieregularny kształt trzeba wrzucić taką linijkę:
ExtGenerateMask(CoolForm.Picture.Bitmap, clWhite, filename);
gdzie <filename> jest nazwą pliku do którego zapisywane są jakieś tam ustawienia :>; trzeba jeszcze dodać moduł w sekcju uses <ExtMaskGenerator>
przynajmniej u mnie tak działa... bez tej linijki wychodzi zawsze zwykły prostokąt :]
Podoba mi się (chociaż jak Adam całego nie przeczytałem). Widać, że jest jeszcze ktoś w tym serwisie, kogo interesuje rozwój działu Delphi (od doś dawna zresztą nieaktualizowanego, również przeze mnie :-P )
planowalem zrobic alternatywe dla xml'a, nie bynajmniej zeby wypuscic nowy/lepszy jezyk (bo to niemozliwe) ale zeby zaimponowac samemu sobie :d
hmm ponad 70 odslon w ciagu 6 godzin, to duzo czy malo? :d
Nie czytałem całego ani nie przyglądałem się kodu (Sorki ;)) ale całkiem niezle to wyglada. Aha... jako formatu skorek i ogolnie - danych - uzywa sie teraz XML :)
Jest to moj pierwszy artykul i prosil bym o ocene lub komentarz :P
Na C to daj to bedzie ok.