Kontrolki ciąg dalszy…

Po niedawnych zmianach w sposobie pozycjonowania kontrolek na oknie przestały działać Layouty, napisane przeze mnie wcześniej. Postanowiłem ten stan rzeczy zmienić pomimo tego, że wydawało mi się to ( przy aktualnym pozycjonowaniu ) dość trudne. Okazało się jednak, że strach ma wielkie oczy ponieważ nie tylko nie było to trudne, ale wręcz dziecinnie proste! Tak więc znów można layoutować okienka.
Oczywiście nie zapomniałem także o wczytywaniu GUI z pliku. Tam też można tworzyć layout’y i ustawiać zachowanie kontrolek. Okienko z prawej strony zostało stworzone za pomocą takiego kodu:
progressBar pb
{
	value:		90%
}

checkBox cb
{
	title:		Check Box
	checked:	true
}

trackbar tb
{
	min:		100
	max: 		120
	value:		110
}

editbox edit2
{
	title:		100
}

button applyButton
{
	title:		Close
	policy:		minimum
	maxsize:	0, 22
	minsize:	75, 22
}

# LAYOUT SETUP

hlayout hLay
{
	spacing:	8
	addchild:	edit2
	addchild:	applyButton
}

vlayout vLay
{
	margin:		8
	addchild:	tb
	addchild:	hLay
	addchild:	cb
	addchild:	pb
}

window testWnd
{
	title:		Test Window
	position:	50, 50
	size:		200, 200
	layout:		vLay
}
Kod jest chyba na tyle prosty i intuicyjny, że nie muszę niczego tłumaczyć ^^ Powiem tylko, że jeśli wystarczą nam domyślne ustawienia kontrolki i nie chcemy definiować jakichś specyficznych możemy pominąć blok { … }. Przykładowo deklaracja progress bar’a z ustawieniami domyslnymi wyglądałaby tak:

progressbar testProgressBar

To tyle na razie, później może zajmę się usprawnianiem i ulepszaniem jeszcze czegoś innego 😛

Window Manager i Control Loader

Free Image Hosting at www.ImageShack.us

Dzisiaj wziąłem się za pisanie i skończyłem prostego menadżera okien. Właściwie to jest on na prawdę bardzo prosty ponieważ potrafi jedynie rozpoznać do którego okna wysyłać komunikaty i przesuwać aktywne okna na wierzch. Napisanie czegoś takiego nie było specjalnie dużym wyzwaniem 😉

Napisałem także małe co nieco do wczytywania kontrolek z pliku. Właściwie to do tworzenia kontrolek na podstawie opisu w pliku. Format jest dziecinnie prosty, przykładowo chcąc stworzyć okienko z jednym przyciskiem trzeba napisać:

window NazwaOkna
{
	title: Tytuł Okna
	position: 100, 100
	size: 250, 200
}

button NazwaPrzycisku
{
	title: Tekst Przycisku
	position: 10, 10
	size: 100, 22
	parent: NazwaOkna
}

Można pokusić się o stwierdzenie, że obsługa jest intuicyjna 😉 Aktualnie ustawiać można jedynie te opcje, które są ogólne dla wszystkich kontrolek. Moim następnym krokiem będzie dodanie możliwości ustawienia wartości specyficznych dla konkretnych kontrolek takich jak na przykład definiowanie czy Check Box jest zaznaczony czy też nie.

Przebudowa GUI

Postanowiłem odświeżyć nieco GUI w moim lib-enginie 😉 Wszystko przez różne koncepcje na wszystko co tam kodziłem. Zrobił się z tego niemały bałagan. Dodatkowo postanowiłem przy okazji zmienić jedno rozwiązanie dotyczące rysowania ponieważ w poprzednim pojawiało się mnóstwo artefaktów, z których najczęstszym było rysowanie kontrolek poza oknem.

Oczywiście oprócz samego narysowania GUI ważna jest także prędkość jego odrysowywania. Z tego też powodu nie zrobiłem przycinania obiektów okna znajdujących się poza jego obszarem, choć nie będą otrzymywać żadnych komunikatów to nadal będą rysowane.

Wcześniej napisałem sobie guiLayout która jest klasą bazową dla klas ustawiających kontrolki na oknie. Nie jestem jednak pewien czy będę dalej trzymał się tego podejścia. Prawdopodobnie stworzę jakiś edytor okien dzięki któremu będzie można sobie takie okno zaprojektować. To oczywiście pociągnie za sobą stworzenie jakiegoś pliku w którym zapisywane będą dane o oknach. Prawdopodobnie będzie to plik tekstowy, nad którego formatem zastanawiam się już od jakiegoś czasu.

Zanim jednak zrobię cokolwiek w kierunku ułatwiania tworzenia okien, muszę poprawić i wyczyścić aktualny kod, tak aby GUI działało prawidłowo i szybko 🙂

Delegaty i Koło

Pisałem już wcześniej o wykorzystaniu delegatów do obsługi zdarzeń GUI a AGE. Jednak po zabawie z `upakowywaniem` AGE do DLL-ki coś się posypało i delegaty nagle przestały chcieć działać.

Na forum też nie bardzo było wiadomo o co tak na prawdę chodzi z tym błędem… Teoretycznie kod który napisałem do obsługi delegatów był OK. W programach testowych działało i w AGE wcześniej też działało, ale coś musiałem popsuć 😛 Ludzie namawiali mnie oczywiście do wykorzystania już istniejących, sprawdzonych i działających klas, jednak nie chciałem specjalnie włączać do AGE czegoś dużego. Ktoś w końcu rzucił hasłem FastDelegate (wyróżniając się wśród rzeszy fanów boost’a 😉 ) i postanowiłem to wypróbować.

Chwila nauki obsługi. Potem dłuższa chwila montowania w AGE i… nie działa! Na szczęście znów Xion zwrócił mi uwagę na to, że można łatwiej i przejrzyściej. Skorzystałem z niej.

Teraz już wszystko działa (przynajmniej w moich testach działało) w DLLce też ładnie chodzi, a odpowiedź na pytanie dlaczego wcześniejsze rozwiązanie nie działało pozostanie chyba bez odpowiedzi.

Delegaty z GUI

Tak jak mi poradził Xion poszukałem troszkę o delegatach. Wcześniej nie chciało mi się tego uczyć bo uznawałem to za czarną magię, ale jak się okazało – nie taki diabeł straszny… 🙂

Zacząłem od zrozumienia jak to w ogóle działa. Później poszukałem informacji w jaki sposób można to zakodzić w C++. Napisałem sobie najpierw prosty program realizujący coś takiego, a potem malutką klasę która mi opakowuje to wszystko (raptem 1 metoda i operator() ). Udało mi się to wszystko nakłonić do działania.

Teraz przyszło najtrudniejsze – wcisnąć to do AGE.

Tutaj już trochę bardziej musiałem się wysilić i koncepcji było kilka. Oczywiście najprostsza, najbardziej oczywista i najwygodniejsza metoda przyszła mi do głowy na sam koniec. Ostatecznie jedyne co trzeba zrobić to napisać linijkę:

CONNECT( button, CM_CLICKED, task, gameTask::buttonClicked );

Jest to makro, które powstało – jak to makra mają w zwyczaju – dla wygody 😉 Oczywiście warto też napisać metodę buttonClicked() ^^

Dodatkową zaletą wykorzystania delegatów jest to, że dla zdarzeń dla, których nie określę żadnej metody nie są wywoływane żadne funkcje. W poprzedniej metodzie – a’la procedura okna – funkcja była wywoływana nawet dla nieobsługiwanych zdarzeń, a to zawsze jakaś strata czasu. Szczególnie dla zdarzenia typu CM_MOVE ^^

Tak więc chciałem podziękować Xionowi za jego konstruktywną krytykę mojego rozwiązania i podsunięcie czegoś bardziej cywilizowanego 😉

GUI: Sposób na rysowanie

Podczas pisania obsługi eventów wpadła mi do głowy pewna myśl. Mianowicie, zastanowiłem się po co rysować w każdej klatce całe GUI skoro i tak rzadko kiedy coś się tam zmienia.

A może by tak zrenderować GUI  do tekstury i wyświetlać tylko teksturę? Takie rozwiązanie znacznie zwiększyłoby wydajność rysowania takiego GUI. Nawet idąc na łatwiznę i odrysowując całe GUI gdy tylko jeden przycisk zmieni swój kolor bo myszka jest akurat nad nim zyskujemy całą masę przebiegów, w których jednak nic ciekawego się nie dzieje.

Pokombinuję z tym trochę i pochwalę się efektami bądź ich brakiem ^^

GUI events

Popełniłem kawałek kodu za który strażnicy obiektowości od razu posłaliby mnie do karceru. Mogę jednak swoją ignorancję uzasadnić w prosty sposób: Gdybym pisał to zgodnie z zasadami OOP to użytkownik w późniejszym czasie używający tego co pięknie napisałem stwierdziłby, że używa się tego dość ciężko.

Piszę oczywiście o tej nieszczęsnej obsłudze zdarzeń pochodzących z GUI. Kilka postów wcześniej rozważałem sposoby na rozwiązanie tego problemu i doszedłem do wniosku (co zresztą doradzał mi też Tarains) że najlepiej będzie stworzyć coś a’la MS’owa procedura okna.

Po zakodzeniu tego w AGE i wypróbowaniu widzę, że jest to całkiem przyjemny sposób, nie mówiąc już o tym, że nie trzeba co klatkę sprawdzać czy coś się stało z GUI.

Muszę przyznać, że jestem całkiem zadowolony ze wstępnych testów tego rozwiązania. Spróbuję jeszcze stworzyć trochę większe GUI żeby zobaczyć jak mocno rozrośnie mi się kod.