Strona używa cookies (ciasteczek). Dowiedz się więcej o celu ich używania i zmianach ustawień. Korzystając ze strony wyrażasz zgodę na używanie cookies, zgodnie z aktualnymi ustawieniami przeglądarki.    X

Libgreattao: dynamiczne zmiany interfejsu w locie

W tym wpisie opiszę zmiany, jakie zaszły w standardowym trybie pracy libgreattao. Chodzi o priorytety i switches. Priorytety pozwalają na określenie uszczegółowienia sposobu przetwarzania pewnej ścieżki. Switches pozwalają na zmianę interfejsu w zależności od wewnętrznych stanów lub interakcji z użytkownikiem.

Dla zademonstrowania tego, co zaszło, posłużę się przykładami.

Przykład pierwszy na priorytety - wydzielamy przycisk cancel

W tym celu przerobimy okno /desktop/dialogs/question_dialog dla wzornictwa podstawowego, nazwanego mWidgets.

Tak wygląda kod wspomnianej klasy, dostarczany wraz z biblioteką. <root> <label dir-for="label" path="/message" /> <label dir-for="description" path="/message" /> <hbox> <template path="/actions/*"> <button label="ok" dir-for="event,label,shortcut"> <attr-connect name="label" function="last-dir" /> </button> </template> </hbox> </root>

Przerobimy go na taki: <root> <label dir-for="label" path="/message" /> <label dir-for="description" path="/message" /> <hbox> <template path="/actions/*" priority="0"> <button label="ok" dir-for="event,label,shortcut"> <attr-connect name="label" function="last-dir" /> </button> </template> </hbox> <hbox> <template path="/actions/cancel" priority="1"> <button label="Cancel" dir-for="event,label,shortcut" /> </template> <template path="/actions/exit" priority="1"> <button label="Exit" dir-for="event,label,shortcut" /> </template> <template path="/actions/ok" priority="1"> <button label="Exit" dir-for="event,label,shortcut" /> </template> </hobx> </root>

W ten sposób przyciski exit, cancel i ok będą wydzielone i wyświetlane poniżej. Priorytety poza tym dają możliwość spersonalizowania elementów, jak np. ustawienia
własnej etykiety czy ikony.

Pierwszy przykład wykorzystanie przełączników na podstawie programu testowego

Przełączniki zostały wykorzystane w dwóch trzech testowych. Pierwszy typ przełączników, to przełączniki pozwalające na zmienienie widoku ze względu na wartość zmiennej. Zaprezentowana niżej klasa okna zawiera szablony do akcji, lecz przełączanie widoku jest realizowane całkowicie przez libgreattao. Użytkownik tylko naciska przycisk.Jedyne, co musi zrobić aplikacja, to zainicjować libgreattao, utworzyć okno, a następnie uruchomić domyślną pętlę komunikatów. Przykładu kodu programu nie przedstawię. <root> <variable name="zero" action="change" value="0" /> <variable name="one" action="change" value="1" /> <variable name="state" action="change" value="0" /> <variable name="true" action="change" value="TRUE" /> <variable name="false" action="change" value="FALSE" /> <variable name="is_first_if_processed" action="change" value="FALSE" /> <button label="toggle"> <handler> <if type="equal" variable1="state" variable2="zero"> <variable name="state" action="change" value="1" /> <variable name="is_first_if_processed" action="change" value="TRUE" /> </if> <if type="equal" variable1="is_first_if_processed" variable2="false"> <if type="equal" variable1="state" variable2="one"> <variable name="state" action="change" value="0" /> </if> </if> <variable name="is_first_if_processed" action="change" value="FALSE" /> </handler> </button> <switch variable="state"> <if type="equal" variable1=".switch_value" variable2="zero"> <label label="0" /> </if> <if type="equal" variable1=".switch_value" variable2="one"> <label label="1" /> </if> </switch> </root> Pierwszymi dziećmi elementu root są zmienne. Ponieważ każda wartość musi być przechowywana w pamięci, to postanowiłem, że do porównania dwóch wartości potrzebne są dwie zmienne. Najpierw definiujemy zmienne dla stanu okna. Następnie domyślny widok(zmienna state). Następne zmienne są nam potrzebne do pewnej sztuczki, ponieważ nie obsługujemy słowa kluczowego else. Sztuczka była omawiana w jednym z poprzednich wpisów. Co ten kod powoduje? Zawartość elementu handler, w naszym przykładzie, określa zachowanie domyślnego zdarzenia przycisku, którego element jest dzieckiem. Ustawiamy w nim zmienną state. Zmiana tej zmiennej spowoduje usunięcie zawartości wygenerowanej dla naszego okna przez switch, a następnie wygenerowanie nowej zawartości, przetwarzając ten switch, Switch dla swoich potomków określa zmienną .switch_value, która zawiera wartość zmiennej state. W ten sposób generujemy nową etykietę za każdym razem, gdy ktoś naciśnie przycisk.

Drugi przykład wykorzystania przełączników na podstawie programu testowego

W tym wypadku obsłużymy również szablony. Po prostu użytkownik wybiera menu view, a następnie tryb widoku. Jedyne, co musi zrobić aplikacja, to zainicjować libgreattao, utworzyć okno, stworzyć elementy, a następnie uruchomić domyślną pętlę komunikatów. Przykładu kodu programu nie przedstawię. <root> <variable name="zero" action="change" value="0" /> <variable name="one" action="change" value="1" /> <variable name="state" action="change" value="0" /> <menu> <submenu label="View"> <item label="Normal"> <handler> <variable action="change" name="state" value="0" /> </handler> </item> <item label="Compact"> <handler> <variable action="change" name="state" value="1" /> </handler> </item> </submenu> </menu> <switch variable="state"> <label label="First test" /> <if type="equal" variable1=".switch_value" variable2="zero"> <label label="One" /> <template path="/actions/*"> <button dir-for="label,event"> <attr-connect name="label" function="last-dir" /> </button> </template> </if> <if type="equal" variable1=".switch_value" variable2="one"> <label label="Two" /> <template path="/actions/*"> <button dir-for="label,event" /> </template> <template path="/actions/cancel" priority="1"> <label label="You cannot cancel this action in this view" /> <label> <attr-connect name="label" function="last-dir"/> </label> </template> </if> </switch> <template path="/actions/*"> <label label="a" /> <switch variable="state"> <if type="equal" variable1=".switch_value" variable2="zero"> <button dir-for="label,event" /> </if> <if type="equal" variable1=".switch_value" variable2="one"> <label label="test" dir-for="label" /> </if> </switch> </template> </root> W tym wypadku wykorzystujemy i przełączniki, jak również priorytety. Jeżeli użytkownik wybierze drugi widok(compact), to wtedy na górze okna nie zostanie wyświetlony przycisk cancel, a jedynie napis. Pozostałe przyciski zostaną obsłużone normalnie.

Kolejny przykład zastosowania przełączników - dostosowujące się samo okno z zapytaniem

Teraz inny rodzaj przełączników - przełączniki bazujące na ilości dopasowań ścieżek do wzoru. Zaczniemy od kodu: <root> <variable action="change" name="two" value="2" /> <variable action="change" name="one" value="1" /> <variable action="change" name="zero" value="0" /> <label dir-for="label" path="/message" /> <label dir-for="description" path="/message" /> <switch name="/actions/*" function="path-matched"> <label label="Hello, world!" /> <if type="greater" variable1=".switch_value" variable2="one"> <hbox> <template path="/actions/*"> <button dir-for="label,event"> <attr-connect name="label" function="last-dir" /> </button> </template> </hbox> </if> <if type="smaller" variable1=".switch_value" variable2="two"> <vbox> <template path="/actions/*"> <label label="Option:" /> <button dir-for="label,event"> <attr-connect name="label" function="last-dir" /> </button> </template> </vbox> </if> </switch> </root> Jak to działa? Taki przełącznik ma inne atrybuty niż dla poprzedniego typu. Musi być ustawiony atrybut function na path-matched, a dodatkowo atrybut name musi być ustawiony na wzór do porównywania. Gdy zmieni się liczba elementów pasujących do wzoru, to wtedy nastąpi usunięcie wszystkich elementów wygenerowanych podczas przetwarzania switches, a następnie ponowne przetworzenie switches - bardzo prosty mechanizm!
W tym wypadku napisałem prostą aplikację, która tworzy i usuwa przycisk exit. Oto jej kod źródłowy: #include <libgreattao/tao.h> #include <stdlib.h> char show_option= 0; static void exit_from_app(void *mess1, void *mess2) { exit(0); } static void toggle(void *mesh1, void *window) { show_option ^= 1; if (show_option) { tao_add_handler(window, "/actions/exit", exit_from_app, NULL); } else { tao_delete_handlers(window, "/actions/exit"); } } int main(int argc, char **argv) { void *window; tao_initialize("Libgreattao test for morphing window 1", "", &argc, argv); tao_set_alternative_design("tests-design/"); window = tao_new_window("/switches/question_dialog"); tao_add_handler(window, "/:abort", exit_from_app, NULL); tao_add_handler(window, "/actions/toggle", toggle, window); tao_handle_events(); } Jak widać, po naciśnięciu przycisku toggle tworzony bądź usuwany jest przycisk exit, a interfejs dokonuje automatycznych przekształceń, raz wyświetlając etykietę Opcje, a innym razem nie.

Co dalej?

Priorytety, jak i przełączniki, można wykorzystać do różnych celów. W przyszłości połączę widoki /desktop/dialogs/question_dialog wzornictwa domyślnego i modern w taki sposób, że po wyborze wzornictwa modern przyciski będą wyświetlane, jak we wzornictwie domyślnym, jeśli będzie ich mało, a w przypadku dużej liczby przycisków, będą wyświetlane, jak w wzornictwie modern, dodatkowo będą stosowane priorytety w przypadku dużej liczby przycisków, by exit, itd. były wyświetlane poza listą rozwijaną.

Planuję również zaimplementować widok ikon i wykorzystać priorytety i przełączniki w /app/file_manager, by umożliwić użytkownikowi przełączanie widoku listy/ikon bez konieczności stosowania zmian w aplikacji.

To już wszystko!

Edycja 11.11.2015:
Poniżej zaznaczam film demonstrujący, jak to działa

 

linux inne

Komentarze

0 nowych
nintyfan   10 #1 08.11.2015 15:24

Przepraszam, że nie zamieściłem grafik, ale wtedy wpis byłby znacznie obszerniejsze.

Jedno pytanie: czy nie warto dodać do tytułu „(zmiany niezależne od aplikacji)”?

kasu.   2 #2 08.11.2015 19:39

Co to za język?

nintyfan   10 #3 08.11.2015 19:50

@kasu.: Na studiach mnie uczono, że język musi mieć możliwość tworzenia własnych słów. Jest to mój własny "język", wzorowany na XML-u, ale nie posiada możliwości definiowania nowych słów.

Chyba, że chodziło Ci o C(ostatni listing).

Autor edytował komentarz.
__Tux__   13 #4 09.11.2015 09:59

@nintyfan: To po prostu jest XML, on polega właśnie na definiowaniu własnych tagów.

nintyfan   10 #5 09.11.2015 10:03

@__Tux__: Tak, ale w mojej aplikacji xml brakuje prologu xml-a. Z tego, co wiem, to jest on konieczny.

__Tux__   13 #6 09.11.2015 11:12

@nintyfan: Mi jeszcze trochę brakuje więcej kodu z C. Rozumiem, że chodziło przede wszystkim o pliki XMLowe, ale nie obraziłbym się za bardziej rozbudowaną apkę.

nintyfan   10 #7 09.11.2015 11:21

@__Tux__: Stworzyłem tao-multishell - jest to chyba najbardziej rozbudowana aplikacja w libgreattao, jaką stworzyłem. Ten wpis specjalnie nie został przywiązany do tagu programowanie, bo nie traktuje o programowaniu. API libgreattao jest dosyć proste i przejrzyste - praktycznie wszystkie ezportowane symbole są umieszczone w pliku main.c, a wszystkie eksportowane symbole rozpoczynają się od ciągu tao_.

  #8 09.11.2015 11:23

screenshoty albo video by się przydało, żeby widać było co i jak.

nintyfan   10 #9 11.11.2015 12:45

@Anonim (niezalogowany): Zrobione.