Blog (76)
Komentarze (5.6k)
Recenzje (0)
@nintyfanLibgreattao: dynamiczne zmiany interfejsu w locie

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:

[code=C] #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();

} [/code]

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 [youtube=https://www.youtube.com/watch?v=q3LRHIg2B2w&feature=youtu.be]

Wybrane dla Ciebie
Komentarze (9)