Wątek przeniesiony 2021-01-26 17:57 z Projekty Forumowe przez furious programming.

Implementacja systemowych przycisków Win10, z funkcją animowanych przejść

4

Powoli zbliżam się do końca implementacji Richtrisa w wersji 2.0 (spoko, napiszę o tym na blogu). Główne jego okno jest pozbawione obramowania, a przez to również systemowych przycisków do minimalizacji, przywracania i zamykania okna.

Pomyślałem więc, że sobie takie przyciski zaprogramuję, czyli napiszę odpowiednie klasy (w formie ~komponentu) oraz w konstruktorze klasy formularza stworzę je dynamicznie. Wszystko dlatego, że nie chciało mi się babrać z pakietami i reinstalacją środowiska, żeby mieć je na palecie komponentów i w designerze). Łącznie z pięć — minimalizacja, zamknięcie oraz trzy do innych potrzebnych funkcji, całość w formie fajnego paska narzędzi. Przyciski te są bardzo proste — trzy główne stany (normalny, ”gorący” i wciśnięty) można uprościć do trzech obrazków, podmienianych w odpowiednich zdarzeniach. Roboty na pół godziny.

buttons.png

Ale… Naszło mnie, aby zaimplementować animowany efekt przejścia, wykonywany po najechaniu i zdjęciu kursora — taki sam jak ma np. okno eksploratora. Do łączenia obrazów na potrzeby przejścia, wykorzystałem kod z projektu testowego, którego źródła jakiś czas temu tutaj wrzucałem — Łączenie dwóch obrazów w jeden na podstawie zadanego poziomu. No i te animacje wcale nie były tak trywialne do zaprogramowania, jak mi się na początku wydawało — miałem niemałą zagwozdkę… ;)

Było trochę zabawy z timerem i liczeniem klatek, tak aby w trakcie animacji klasa potrafiła wstrzymać trwające przejście, odwrócić jego kierunek i wznowić, ale koniec końców dało się zrobić. Dawno temu próbowałem coś takiego zrobić, ale zarzuciłem prace, bo mnie te przejścia denerwowały — zawsze się coś zacinało i tak wisiało. A tu się okazało, że trzeba sporo kodu naklepać, dla z pozoru tak prostego bajeru. W dodatku żeby miało to prawo działać, trzeba było użyć pięciu bitmap — jednej instancji do dynamicznego malowania i czterech referencji — i odpowiednio nimi żonglować. oczywiście przejście wykonywane jest jedynie w CMMouseEnter i CMMouseLeave — animacje podczas klikania nie są wykorzystywane, zgodnie z zachowaniem systemowych kontrolek.

W każdym razie z efektu końcowego jestem zadowolony — wyszło idealnie. Kody źródłowe kontrolki oraz aplikacji testowej (oraz plik wykonywalny do zabawy dla nie-pascalowców) są w załącznikach. Demówka pokazuje dwa okna, jedno z imitacją czarnego motywu oraz drugie z imitacją jasnego. Grafiki przycisków identyczne jak w systemie. W razie gdyby ktoś chciał mieć tę kontrolkę na palecie, to trzeba kilka rzeczy przenieść do sekcji published, dodać co nieco dla designera i sprawdzić jak w designerze to działa. Sam nie używam w Richtrisie komponentów wizualnych, więc tworzenie tych przycisków z poziomu kodu mi w zupełności wystarczy.

No, kod do dowolnego użytku, więc częstujcie się i miłej zabawy. Dotacje mile widziane.


PS: Przyciski nie mają przypisanej żadnej akcji po kliknięciu, więc aby zamknąć program, należy zamknąć czarną formę za pomocą Alt+F4. Nie programowałem klikania tych przycisków, aby móc testować poprawność ich działania. I wy też możecie śmiało klikać — demówka ani się nie zamknie, ani nie zminimalizuje.

PPS: W razie gdyby ktoś chciał sprawdzić w zwolnionym tempie i podrażnić się nieco najeżdżając i zabierając kursor, to zmieńcie sobie wartość prywatnej stałej TWindowButtonFade.FADE_FRAMES np. na 100. Animacja wykonywać się będzie bardzo długo, więc będzie czas na zabawę.

PPPS: Przy bardzo szybkim ruszaniu kursorem, zdarza się, że CMMouseLeave nie jest wywoływany. Nie jest to wina kodu komponentu, a LCL — zgłaszałem ten problem lata temu i nadal nic się w tym temacie nie zmieniło. Ludzie nie potrafili nawet tego zreprodukować, i chyba przez to olano sprawę. Ja to mam szczęście… :D

3

trzy główne stany (normalny, ”gorący” i wciśnięty)

Jeszcze bym dodał disabled. Można przyciski nieaktywne ukrywać/chować, ale znikające guziczki z UI (czasami mam 4, czasem 5, ten środkowy sobie znika) będą bardzo nieintuicyjne i niezgodne z zasadami projektowania UI.

aby zaimplementować animowany efekt przejścia

Jednocześnie będzie aktywna tylko jedna animacja, czy wiele? Co w sytuacji, w której ktoś będzie sobie machać kursorem po przyciskach - co ćwierć sekundy będziesz miał rozpoczęcie animacji kolejnego przycisku, i tak w kółko. Albo np. animacja zapalania zostanie wyzwolona zanim zakończy się wygasanie?
Kiedyś robiłem aplikację, która miała kilkanaście/kilkadziesiąt animowanych elementów (ikonki sygnalizujące stan pewnych urządzeń). Chciałem pójść na łatwiznę i skorzystałem z jakiegoś gotowego komponentu do wyświetlania GIF'ów. Przy tak ok. 10 sztukach było OK, ale przy większej ilości zaczęły zwalniać, a przy ok. 20 całość przestawała działać, nie pojawiały się albo przestawały animować. Każdy komponent miał własnego timera, który odpowiadał za jego animację. Przy większej ilości to się wychrzaniało. W końcu zrobiłem to tak, że stworzyłem jednego timera do animacji, a wszystkie zadania dawałem do kolejki/listy. Potem ten timer odczytywał zadania na liście i je wykonywał. Dla każdego elementu był jedynie jeden wpis. Jeśli np. dany przycisk był w trakcie wygaszania, a działanie użytkownika miało go zapalić, to jedynie zmieniałem na tej liście kierunek animacji i od kolejnego odpalenia timera ta kontrolka była animowana w drugą stronę.

A tu się okazało, że trzeba sporo kodu naklepać, dla z pozoru tak prostego bajeru

:D Zgadza się, patrz poprzedni punkt ;)

Poza tym - taka uwaga/prośba/sugestia - jak dajesz EXE do przetestowania, to dołącz do ZIP'a wszystko, co potrzebne do poprawnego działania. Chciałem zobaczyć jak ten efekt wygląda i po odpaleniu dostałem poniższy komunikat. Oczywiście - poradziłem sobie, bo ten folder był w ZIP'ie ze źródłami, ale mimo wszystko - lepiej to dodać.

screenshot-20210126085951.png

Ale tak ogólnie to dobra robota :)
Powiedz (nie miałem czasu zaglądać w źródła) - czy to wszystko działa w oparciu o LCL, czy schodzisz niżej na poziom WinApi? W sensie - czy na innych platformach powinno pójść?

1
cerrato napisał(a):

Jeszcze bym dodał disabled.

Tak, disabled to czwarty stan używany w systemie, ale sam takiego nie używam, więc w mojej aplikacji testowej (i tej kontrolce) go nie uwzględniałem. W Richtris mam tych przycisków pięć w formie małego toolbara, dwa imitujące systemowe oraz trzy dodatkowe funkcje i wszystkie zawsze są aktywne, dostępne do użycia:

richtris.png

Dodanie wsparcia disabled to dość prosta modyfikacja, bo wystarczy dorzucić jeszcze jedną bitmapkę do zestawu i w nadpisanej metodzie SetEnabled włączyć/wyłączyć timer, ustawić co nieco i oraz przemalować kontrolkę. To wszystko.

aby zaimplementować animowany efekt przejścia

Jednocześnie będzie aktywna tylko jedna animacja, czy wiele?

Tzn. dany przycisk może wykonać jedną animację — albo przechodzenia ze stanu normal do hot (CMMouseEnter), albo w drugą stronę, czyli ze stanu hot do normal (CMMouseLeave). Natomiast każdy przycisk używa swojego timera, więc jeśli jest ich dużo na formie i śmiga się po nich kursorem, to kilka przycisków animuje się jednocześnie.

Co w sytuacji, w której ktoś będzie sobie machać kursorem po przyciskach […]

Będą się animować niezależnie i jednocześnie. ;)

Albo np. animacja zapalania zostanie wyzwolona zanim zakończy się wygasanie?

To też jest zabezpieczone. Jeśli najedzie się kursorem, to wyzwolona zostanie animacja zapalania. Jeśli w jej trakcie zdejmie się kursor znad kontrolki, to obecna animacja się zatrzyma, a ta zanikania zostanie włączona. I to nie że nagle przycisk się zapali i zacznie gasnąć od pełnej jasności (czyli nie nastąpi skok z bieżącej jasności do maksymalnej i dopiero wtedy zacznie gasnąć), a od bieżącej jasności animacja gaśnięcia będzie wykonana.

Dlatego pisałem, że można wydłużyć czas trwania animacji i podrażnić się z timerem, najeżdżając kursorem na kontrolkę i go zabierając. Jeśli dana animacja nie zdąży się wykonać do końca, to w kółko będzie zatrzymywana, a proces animowania odwracany. I dlatego właśnie dużo kodu trzeba było napisać i aż pięć referencji wykorzystać, aby było to możliwe.

Kiedyś robiłem aplikację, która miała kilkanaście/kilkadziesiąt animowanych elementów (ikonki sygnalizujące stan pewnych urządzeń). Chciałem pójść na łatwiznę i skorzystałem z jakiegoś gotowego komponentu do wyświetlania GIF'ów. […] Przy tak ok. 10 sztukach było OK, ale przy większej ilości zaczęły zwalniać, a przy ok. 20 całość przestawała działać, nie pojawiały się albo przestawały animować. Każdy komponent miał własnego timera, […]

A to ciekawe. Nie wiem czy jest ustalony jakiś konkretny limit na jednocześnie działające timery w obrębie danego procesu (wątpię), ale sam nie testowałem jak zachowa się np. 100 moich przycisków na jednej formie. Mimo wszystko pamiętaj, że mój przycisk to nie GIF — animacja wykonywana jest jedynie podczas interakcji z kursorem. Jeśli nie rusza się kursorem nad przyciskami to żaden timer nie działa (ma Enabled na False), dzięki czemu przyciski kompletnie nic nie robią.

Poza tym - taka uwaga/prośba/sugestia - jak dajesz EXE do przetestowania, to dołącz do ZIP'a wszystko, co potrzebne do poprawnego działania.

Nie no znowu… Już po raz kolejny wrzucam jakąś demówkę i zapominam dodać do archiwum wymagane pliki… Za chwilę dorzucę wszystko co trzeba i zaktualizuję załącznik, tak aby można go potestować. Dzięki za informację. :D

Powiedz (nie miałem czasu zaglądać w źródła) - czy to wszystko działa w oparciu o LCL, czy schodzisz niżej na poziom WinApi? W sensie - czy na innych platformach powinno pójść?

Nie używałem WinAPI, bo nie było takiej potrzeby — to zwykła kontrolka graficzna, używająca zwykłych bitmap i standardowego timera. Wszystkie te elementy są w LCL wieloplatformowe, więc komponent powinien bez problemu działać na wszystkich platformach wspieranych przez LCL (głównie mam tutaj na myśli Windows, Linux, FreeBSD i macOS). I również bez problemu powinno się dać ten kod przenieść do Delphi — kilka zmian będzie potrzebnych, ale głównie kosmetycznych.

Demówka jest przeznaczona na Windows, bo usunąłem zbędne moduły i dyrektywy z głównego pliku projektu. Ale po dołączeniu do uses modułu CThreads, czyli tak jak jest domyślnie, wszystko powinno zadziałać na innych platformach.

0

sam nie testowałem jak zachowa się np. 100 moich przycisków na jednej formie. Mimo wszystko pamiętaj, że mój przycisk to nie GIF — animacja wykonywana jest jedynie podczas interakcji z kursorem

W sumie to mógłbyś (jakbyś się kiedyś nudził, albo nie był na tyle w formie, żeby zrobić coś przydatnego, ale jednak mocno chciał coś napisać) zrobić eksperyment i wrzucić tych przycisków kilkadziesiąt, albo coś koło 100. A potem mógłbyś jeszcze zrobić, żeby się animowały w kółko. I od razu zobaczymy, czy to zjawisko, które było u mnie to efekt ograniczeń systemu, czy kiepsko napisanego komponentu do obsługi GIF'òw ;)

standardowego timera. Wszystkie te elementy są w LCL wieloplatformowe, więc komponent powinien bez problemu działać na wszystkich platformach wspieranych przez LCL (głównie mam tutaj na myśli Windows, Linux, FreeBSD i macOS).

Działać to będzie, ale raczej nie bezproblemowo. Chociażby z tego powodu, że timer na linuksie ma coś koło 10 razy większą rozdzielczość (teraz piszę z głowy, jestem w terenie na komórce, więc mogą być jakieś nieścisłości w tym co pisze, nie mam za bardzo jak sprawdzić). Teoretycznie na Windows można ustalić skok co 1ms, ale realnie to chyba mamy dokładność w okolicy kilkunastu ms. Za to Linuks daje czas zblizony do tego, który się podało.

Sam się kiedyś na tym przejechałem - zrobiłem animowanie jakichś elementów na linuksie i chodziło bardzo ładnie. Po przejściu na Windows animacja spowolniła kilkukrotnie. Musiałem trochę się z tym bawić, bardzo niefajna sprawa :/

1
cerrato napisał(a):

W sumie to mógłbyś (jakbyś się kiedyś nudził, albo nie był na tyle w formie, żeby zrobić coś przydatnego, ale jednak mocno chciał coś napisać) zrobić eksperyment i wrzucić tych przycisków kilkadziesiąt, albo coś koło 100.

Zobaczę — może się skuszę.

Działać to będzie, ale raczej nie bezproblemowo. Chociażby z tego powodu, że timer na linuksie ma coś koło 10 razy większą rozdzielczość […]

No ale w czym problem? Linuks nie jest upośledzony (chyba), milisekunda na tym systemie trwa tyle samo co w innych, więc i timer odliczający przerwy pomiędzy klatkami, nie zacznie w magiczny sposób działać z większą rozdzielczością. 10 milisekund to 10 milisekund — na wszystkich platformach efekt będzie podobny. Podobny, bo:

Teoretycznie na Windows można ustalić skok co 1ms, ale realnie to chyba mamy dokładność w okolicy kilkunastu ms. Za to Linuks daje czas zblizony do tego, który się podało.

Windows ogranicza swój sheduler do rozdzielczości rzędu 10ms — domyślnie mniejszych przerw zrobić nie można. Nijak ma się to do pracy timera, bo ten działa w oparciu o czas, a nie o częstotliwość taktowania CPU. Uniksy mają timery o znacznie wyższej precyzji (mikrosekundowej), więc czas trwania animacji będzie bliższy ustawieniom komponentu, a to raczej zaleta, nie wada. Pamiętaj też, że po to komponent ma właściwości Fade.Frames i Fade.Interval, aby można było sobie dopasować animację do własnych wymagań. A ten komponent to tylko głupi przycisk, nie kluczowy element programu, więc praca z dokładnością co do nanosekundy nie jest wymagana. ;)

Natomiast Windows może wykonywać Sleepa z mniejszymi interwałami niż domyślne 10ms — są dostępne funkcje do zmiany minimalnego interwału i możliwe jest ustawienie jednej milisekundy jako minimum (poczytaj o timerach multimedialnych, jeśli jesteś ciekaw). Wiem, bo korzystałem z nich na potrzeby projektów takich jak Deep Platformer, Richtris i CTCT.


Jako ciekawostka dodam, że w przypadku platformera, łatwiej było odmierzać czas pomiędzy klatkami (oczywiście bez busy waitingu) na Uniksach, ze względu na istnienie funkcji FPNanoSleep, mającej 1000-krotnie większą dokładność od windowsowego Sleep z minimalnym interwałem ustawionym w systemie.

Pod Windows trzeba było przerwy na kilka(naście) milisekund wykonywać standardowym Sleep, a odstępy mniejsze niż milisekunda odmierzać zapętlonym QueryPerformanceCounter, tworząc busy waiting, co powodowało nieco wyższe zużycie CPU, ale na poziomie kilku procent, więc nic nie znaczące. Gdyby uniknąć busy waitingu, framerate nie byłby stabilny i by skakał od mniej więcej 58fps do 64fps — wiem, bo to też sprawdzałem.

1

Windows ogranicza swój sheduler do rozdzielczości rzędu 10ms — domyślnie mniejszych przerw zrobić nie można. Nijak ma się to do pracy timera

Nie zgadzam się, różnica jest zasadnicza.

Jak masz np. przesunąć jakiś element o 300 px to możesz zrobić 150 przesunięć o 2 piksele co 3 ms - chyba nie muszę tłumaczyć, że im mniejsze przesunięcie oraz krótszy interwał, tym to ładniej wygląda i jest płynniejsze. Jeśli tą samą animację przełożysz z linuksa na Windowsa, to zamiast wpisanych 3ms, dostaniesz to 10 (czy kilkanaście - nie pamiętam dokładnie) ms, czyli 3x więcej. Więc teraz masz 2 opcje - albo animacja trwa 3 razy dłużej, albo zwiększasz skok/przesunięcie przy każdym wywołaniu timera. W efekcie dostajesz animację z mniejszą ilością skoków (żeby łączny czas animowania całości pozostał zbliżony), ale za to każda "klatka" to większy przeskok. Efektem tego jest to, że animacja jest gorszej jakości, mniej płynna, bardziej skacząca. Nie musi to być jakaś tragedia, ale jednak jest różnica.

Owszem, masz rację - da się to obejść - ale jest to problematyczne i dokłada dodatkową robotę z czymś tak pozornie trywialnym jak prosta animacja. No i nie da się przełożyć 1 do 1 pomiędzy różnymi ekosystemami.

0
cerrato napisał(a):

Nie zgadzam się, różnica jest zasadnicza.

Jak masz np. przesunąć jakiś element o 300 px to możesz zrobić 150 przesunięć o 2 piksele co 3 ms - chyba nie muszę tłumaczyć, że im mniejsze przesunięcie oraz krótszy interwał, tym to ładniej wygląda i jest płynniejsze.

No dobrze, ale co to ma wspólnego z moim przyciskiem? ;)

Jeśli miałbym zrobić animację przesuwającego się elementu, którego płynność ruchu miałaby być wysoka, to nie używałbym klasy TTimer, a wątku pobocznego i funkcji zwiększających częstotliwość pracy shedulera. Czyli zrobiłbym to co w Richtris czy Deep Platformerze, bo rozwiązanie dobiera się do wymagań projektowych, a nie na odwrót.

Przycisk nie powinien używać animacji wykonywanej w 60 czy 120 klatkach na sekundę, bo jest jedynie nic nie znaczącym elementem interfejsu, którego użytkownik przez 99.999% trwania sesji nawet nie zauważa. Dlatego powinien zużywać jak najmniej mocy obliczeniowej i jak najrzadziej wykonywać swój kod w ramach głównego wątku. Domyślnie ustawione jest 10ms odstępu pomiędzy klatkami i 10 klatek, ale to samo wizualnie da się uzyskać ustawiając 20ms i 7 klatek lub nawet 25ms i 5 klatek — różnica dla okna będzie niezauważalna.

A nawet jeśli się skupi wzrok na przycisku i zauważy żabkowanie animacji, to użytkownik programu i tak będzie miał to w dupie — w końcu przyciski na belce okna służą do klikania, czyli zamykania czy minimalizacji okna, a nie do zabawy.

Owszem, masz rację - da się to obejść - ale jest to problematyczne i dokłada dodatkową robotę z czymś tak pozornie trywialnym jak prosta animacja. No i nie da się przełożyć 1 do 1 pomiędzy różnymi ekosystemami.

Oczywiście że da się to przełożyć 1:1 na inne platformy. W zależności od tego co się pisze, można napisać jeden kod działający identycznie na wszystkich platformach, a można też (lub trzeba) napisać kilka różnych skrawków, gdzie odpowiedni dla danej platformy będzie odblokowywany dyrektywami kompilatora.

Trzeba z dyrektyw korzystać, jeśli dany mechanizm pomiędzy platformami jest zupełnie różny i posiada zupełnie różny zestaw funkcji systemowych do jego programowania/używania. Jednak aby zrobić to dobrze, potrzebna jest wiedza na temat platform docelowych i tego jak dane mechanizmy działają — na pałę to sobie można węgiel w piwnicy przerzucać. :P

Z Twojej wypowiedzi wnioskuję, że masz jakieś dziwne podejście do tego. Nie wiem dlaczego dziwi Cię to i uważasz to za wadę, że dane rozwiązanie ma ograniczenia, a uniknięcie tych ograniczeń wiąże się z większym nakładem prac. Przecież to oczywiste, że im lepiej ma coś działać i im więcej platform wspierać, tym więcej wiedzy i kodu potrzeba, aby było to możliwe. Nie ma nic za darmo — wiadomo.

1

Sprawdziłem jak działają te timery w liczbie znacznie przekraczającej użyteczność praktyczną i… wszystko działa bez problemu. Przerobiłem demówkę i w białym oknie umieściłem 100 podstawowych przycisków reagujących na ruch kursora i klikanie, a w czarnym oknie umieściłem drugie 100 przycisków, które same w kółko się animują i nie reagują na input użytkownika.

crazy buttons.png

Wszystko działa prawidłowo — czarny formularz mruga jak choinka i nic się nie blokuje (100 timerów działa jednocześnie, z interwałem 10ms), natomiast w białym oknie można bawić się kursorem i też ładnie się wszystko animuje. Tak więc, @cerrato, w swojej demówce o której wspominałeś, musiałeś mieć coś nie tak, bo zbyt duża liczba timerów nie ma prawa zawiesić animacji, jeśli kod jest poprawny. ;)

Oczywiście to nie tak, że można tych timerów odpalić nie wiadomo ile, bo procesor ma skończoną liczbę cykli. O ile same timery nie są zasobożerne, tak akcje wykonywane w ich zdarzeniach z reguły zabierają jakąś część czasu głównego wątku procesu. Przy odpowiednio dużej liczbie wykonywanych zdarzeń, zużycie mocy CPU może urosnąć za bardzo, a przez to dokładność pracy timerów znacząco się zmniejszy, spowalniając animacje i psując końcowy efekt.

W załącznikach źródła tej demówki oraz drugie archiwum z exekiem i grafikami.


Przy okazji tego testu znalazłem dwa błędy w kodzie komponentu. :D

Pierwszy jest taki, że przy bardzo dużej liczbie gęsto upakowanych przycisków, podczas szybszego ruszania kursorem zdarza się, że zostanie wykonana tylko jedna klatka animacji, a po jej odwróceniu, numer klatki przyjmuje wartość 0. Ta wartość używana jest do obliczania stopnia zmieszania obrazów, a że jest ona wykorzystywana do dzielenia, leci wyjątek EDivideByZero. Trzeba było dodać odpowiedni warunek, aby dzielenie przez zero nigdy nie miało miejsca.

Drugi błąd jest trudny do namierzenia. Podczas ładowania obrazu do właściwości, obiekt obrazu wywołuje zdarzenie OnChange, informując komponent o zmianie. Ten reaguje na to zmieniając rozmiar komponentu na zgodny z podstawowym obrazem (z właściwości Normal). Problem jest taki, że obiekt obrazu po załadowaniu pliku z dysku, posiada prawidłową wysokość, ale zerową szerokość. I ciul wie dlaczego tak jest. :/

0

w swojej demówce o której wspominałeś, musiałeś mieć coś nie tak, bo zbyt duża liczba timerów nie ma prawa zawiesić animacji, jeśli kod jest poprawny

Jak mówiłem - skorzystałem z gotowego komponentu do wyświetlania ruchomych GIF'ów. To, co piszesz potwierdza moje podejrzenie - że tamten kod był kiepsko napisany. Tak w ogóle to fajnie, że chciało Ci się zrobić ten test, fajnie to wygląda. To co - teraz 800 przycisków? ;)

Swoją drogą - automatycznie się animujące przyciski to niezła psychodela, ciężko od tego oderwać wzrok :D

0
cerrato napisał(a):

Jak mówiłem - skorzystałem z gotowego komponentu do wyświetlania ruchomych GIF'ów. To, co piszesz potwierdza moje podejrzenie - że tamten kod był kiepsko napisany.

Pewnie problemem nie były timery odmierzające klatki, a słabe wsparcie GIF-ów zapętlonych lub skopana logika obsługi zapętlania. A może coś innego — trudno powiedzieć, bo nie widziałem źródeł tego komponentu. Ale to w sumie nieistotne, bo skoro nie działało to nie działało.

Tak w ogóle to fajnie, że chciało Ci się zrobić ten test, fajnie to wygląda. To co - teraz 800 przycisków? ;)

100 przycisków animujących się jednocześnie pożera u mnie 35% mocy CPU, a dokładność pracy ich timerów zauważalnie spada. Widać to po zabawie przyciskami w białym oknie — one mają ustawione domyślne 10 klatek animacji z interwałem co 10ms, a animują się dwa razy dłużej niż powinny. Komplet 800 przycisków zeżre pewnie całą moc obliczeniową i efektywność animacji spadnie drastycznie.

Swoją drogą - automatycznie się animujące przyciski to niezła psychodela, ciężko od tego oderwać wzrok :D

To co — teraz 800 przycisków? :D

0

@PrzemysławWiśniewski: wszystko fajnie, ale tu nie chodzi o dodanie przycisków na belce tytułowej okna, a na zrobienie imitacji systemów przycisków w oknie, które nie ma ani belki tytułowej, ani w ogóle żadnego obramowania (czyli bsNone).

1 użytkowników online, w tym zalogowanych: 0, gości: 1