Skończyłem implementować ”wystające” elementy okna – teraz wszystko wygląda tak, jak ma wyglądać. Nie spodziewałem się, że będzie tyle komplikacji związanych ze zmianą kształtu okna, jednak wszystko dało się zrobić. :]
Zrobiłem to w ten sposób, że do kontrolki imitującej tło dodałem opublikowane właściwości, dzięki którym mogę przypisać przyciski do kontrolki tła. W ten sam sposób podpina się wystający panel z przyciskami formularza (prawy górny róg). Dzięki temu podczas modyfikacji komponentu tła (zmiany pozycji i/lub rozmiaru), ten z kolei dostosowuje pozycję wszystkich wystających kontrolek, bo ma do nich dostęp. Wszystko można ustawić z poziomu okna inspektora obiektów, więc wystarczy trochę poklikać.
Następnie zmodyfikowałem kod przycisków zakładek, tak aby zmieniały pozycję po zaznaczeniu i odznaczeniu. Dodałem nową właściwość liczbową, określającą rozmiar przesunięcia (domyślnie 10px
). Trochę zabawy było, aby te przyciski przesuwały się właściwie – w designerze mają nie zmieniać pozycji, podczas działania owszem. Chwilę mi to zajęło, bo zależności było dość sporo i ciągle mi te przyciski uciekały.
Kolejną rzeczą do zrobienia była implementacja metody wyznaczającej region formularza. Szału nima – potrzebna jest wyłącznie referencja formularza. Ten szuka kontrolki tła i jeśli ją znajdzie, to ma dostęp do wszystkich innych wymaganych do obliczeń (do panelu i aktywnego przycisku zakładki). Teraz na podstawie referencji tych rzech komponentów, można wyznaczyć region. I tu niespodzianka – nie trzeba bawić się w ręczne pakowanie współrzędnych wierzchołków do tablicy i używać gołych funkcji systemowych (a tymi bawiłem się wcześniej). Do dyspozycji jest klasa TRegion
i metoda SetShape
. Tak więc ustalenie kształtu okna wykonuje poniższa, krótka i prosta metoda:
procedure TAppForm.UpdateFormShape();
var
LBackground: TBaseWindowBackground;
LGroupBox: TBaseConvexGroupBox;
LButton: TBaseTabButton;
var
LRegion: TRegion;
begin
LBackground := FindBackground();
if Assigned(LBackground) then
begin
LGroupBox := LBackground.ConvexCorner.GroupBox;
LButton := LBackground.ConvexTab.SelectedButton;
LRegion := TRegion.Create();
try
LRegion.AddRectangle(LBackground.BoundsRect);
if Assigned(LGroupBox) then
LRegion.AddRectangle(LGroupBox.BoundsRect);
if Assigned(LButton) then
LRegion.AddRectangle(LButton.BoundsRect);
Self.SetShape(LRegion);
finally
LRegion.Free();
end;
end;
end;
Przy okazji warto zaznaczyć, że klasa TRegion
oraz metoda SetShape
korzystają z aktualnego widgetsetu, więc ten kod jest przenośny na inne platformy. Jeszcze jedno, bo przecież zbyt łatwo być nie może. Klasa TRegion
nie posiada metody AddRectangle
pobierającej w parametrze TRect
– tę dodałem sobie za pomocą helpera. Ta istniejąca pobiera cztery inty, a więc zmusza do pisania długich linijek.
Ostatnią rzeczą było sprawienie, aby każdy formularz przyjmował odpowiedni kształt podczas jego tworzenia (bo wszystkie formularze tworzę dynamicznie, oprócz głównego, czym zarządza obiekt Application
) oraz po klikaniu w przyciski zakładek. Tu drugi problem – wywołanie metody UpdateFormShape
w konstruktorze formularza nie zmienia kształtu okna. Widać na tym etapie okno jeszcze nie jest na gotowe. Natomiast po wykonaniu kodu konstruktora, można już dobrać się do okna i manipulować jego kształtem.
Dlatego też skorzystałem z wirtualnej metody DoShow
– ona wołana jest tuż przed pokazaniem okna na ekranie. W sumie to służy ona wyłącznie do odpalania zdarzenia OnShow
, jednak do mojego celu nadaje się idealnie, więc ją nadpisałem i najpierw wywołałem w niej UpdateFormShape
, a następnie wywołałem bazową za pomocą inherited
. Aby formularz zmieniał kształt również po klikaniu w przyciski zakładek, w zdarzeniu OnClick
przycisków (jedno dla wszystkich w danym oknie) wołam samo UpdateFormShape
. Działa bezbłędnie.
To w sumie tyle, jeśli chodzi o ten design. Gdyby ktoś kiedyś potrzebował pobawić się w zmianę kształtu okna to kod podany wyżej może być pomocny. Demówkę prezentującą finalny efekt dorzucę później do załączników, jak będę miał chwilkę.
Szkoda tylko, że klasa TRegion
nie umożliwia tworzenia bardziej skomplikowanych kształtów, na podstawie różnych figur. Bo system posiada funkcje CreatePolygonRgn
, CreateRectRgn
, CreateRoundRectRgn
, CreateEllipticRgn
czy CreatePolyPolygonRgn
, a klasa TRegion
ma jedynie metodę do zwykłych prostokątów. Może kiedyś zostanie ona rozszerzona o inne kształty.