Niniejszy artykuł opisuje klasy zawarte w pakiecie com.cisag.pgm.gui. Stanowi on uzupełnienie dokumentacji API (JavaDoc).
Dokument pełni funkcję podręcznika referencyjnego, który zapewnia szybki przegląd oraz szczegółowe informacje dotyczące klas zawartych w pakiecie com.cisag.pgm.gui. W celu przekazania podstawowej wiedzy zawiera rozdział wprowadzający, opisujący istotne komponenty oraz mechanizmy programowania graficznych interfejsów użytkownika. Artykuł kończy się rozdziałem zawierającym różne przykłady programistyczne.
Grupa docelowa
Artykuł jest przeznaczony dla programistów oprogramowania, którzy przy użyciu klas z pakietu com.cisag.pgm.gui chcą tworzyć graficzne interfejsy użytkownika (GUI) dla aplikacji Comarch ERP Enterprise.
Zakłada się znajomość języka programowania Java oraz środowiska deweloperskiego Comarch ERP Enterprise. Wprowadzenie do tworzenia aplikacji dla Comarch ERP Enterprise znajduje się w artykule Podręcznik programowania.
Ponadto wymagane są podstawowe umiejętności w zakresie projektowania graficznych interfejsów dla nowoczesnych systemów informatycznych. Dla osób, które nie zajmowały się dotychczas programowaniem GUI w języku Java, rekomendowane jest dodatkowo Java Tutorial Creating a GUI with JFC/Swing.
Wprowadzenie
Do projektowania graficznych interfejsów użytkownika Comarch ERP Enterprise udostępnia obszar klas Java, który w wielu aspektach jest porównywalny z koncepcją AWT oraz Swing w języku Java. Wiedza oraz techniki programistyczne z tych obszarów mogą być w większości przypadków bezpośrednio zastosowane.
Java API (Application Programming Interface) ukrywa przy tym środowisko klient/serwer, jak również fakt, że klientem może być na przykład przeglądarka obsługująca jedynie HTML oraz JavaScript.
Obszar UI Comarch ERP Enterprise składa się z dwóch pakietów Java:
- com.cisag.pgm.gui
- com.cisag.pgm.dialog
Pakiet com.cisag.pgm.gui udostępnia właściwe API, natomiast pakiet com.cisag.pgm.dialog stanowi jego implementację.
Za pomocą com.cisag.pgm.gui budowane są graficzne interfejsy aplikacji, natomiast com.cisag.pgm.dialog jest zazwyczaj wykorzystywany do rozszerzania obszaru o dodatkowe komponenty. Ponieważ głównym celem podręcznika jest rozwój aplikacji, drugi pakiet jest w dalszej części omawiany jedynie w ograniczonym zakresie.
Podstawowa koncepcja jest analogiczna do AWT: interfejs GUI składa się z różnych komponentów (VisualElement).
Typowymi komponentami są:
- pola tekstowe,
- etykiety,
- przyciski.
Szczególną kategorię komponentów stanowią kontenery (VisualElementContainer). Służą one do grupowania, pozycjonowania oraz wspólnego sterowania innymi komponentami. W tym celu powiązane elementy są dodawane do odpowiedniego kontenera przy użyciu metody add.
Ponieważ kontenery są również komponentami, mogą one być z kolei dodawane do kontenerów nadrzędnych. Interfejs GUI stanowi strukturę drzewiastą złożoną z kontenerów oraz innych komponentów, przy czym najwyższy kontener jest zawsze oknem (Window) lub klasą pochodną.
Drugą podstawową zasadę (również analogicznie do AWT) stanowią menedżery układu. Zamiast przypisywać komponentom stałe rozmiary i współrzędne, obliczanie rozmiaru i położenia odbywa się dynamicznie, tzn. wartości są automatycznie wyznaczane dla danego zakresu treści, użytej wielkości czcionki, rozmiaru okna oraz rozdzielczości ekranu i dostosowywane w przypadku zmian.
Do każdego kontenera przypisany jest menedżer układu, który przejmuje zadanie rozmieszczania elementów. Menedżer układu implementuje w pewnym sensie strategię, według której komponenty kontenera są rozmieszczane, oraz reguluje sposób wykorzystania przestrzeni dostępnej dla kontenera.
W odróżnieniu od bardziej uniwersalnych interfejsów API (AWT, Swing, com.cisag.pgm.dialog), pakiet com.cisag.pgm.gui oferuje również specjalne wsparcie dla programowania aplikacji standardowych w systemie Comarch ERP Enterprise. Obejmuje to wykorzystanie metadanych (repozytorium), implementację wytycznych stylistycznych oraz uproszczenie realizacji zadań standardowych.
Cechy i koncepcje
Elementy i hierarchia kontenerów
Niewielki przykład z często używanymi elementami ma pokazać, w jaki sposób z pojedynczych elementów budowana jest hierarchia (kontenerów).
Przykładowo, wykorzystano cztery elementy standardowe z pakietu com.cisag.pgm.gui:
- Dialog (com.cisag.pgm.gui.Dialog)
- View (com.cisag.pgm.gui.View)
- Button (com.cisag.pgm.gui.Button)
- Label (com.cisag.pgm.gui.Label)
Dialog jest kontenerem typu top-level. Udostępnia przestrzeń dla wszystkich zawartych w nim elementów. Innymi często używanymi kontenerami typu top-level są na przykład okna aplikacji oraz okna dialogowe.
View jest kontenerem pośrednim lub pomocniczym. Służy do realizacji pozycjonowania przycisku (Button) oraz etykiety (Label). Inne kontenery pośrednie lub pomocnicze, takie jak na przykład ScrollPane lub TabbedPane, oferują dodatkowo również dodatkowe właściwości (np. interaktywne).
Button oraz Label należą do elementów atomarnych, tzn. nie służą do umieszczania w nich innych elementów, lecz zazwyczaj prezentują informacje. Często wykorzystywane są także do przyjmowania danych wejściowych, jak ma to miejsce na przykład w polach tekstowych, ComboBoxach oraz tabelach.
Poniższy diagram przedstawia hierarchię kontenerów w przykładzie. Zawiera każdy utworzony lub użyty kontener wraz z elementami, które się w nim znajdują. Należy zwrócić uwagę, że każde okno (Window), a także każdy dialog, tworzy własną, niezależną hierarchię kontenerów.

Hierarchia kontenerów
Z diagramu wynika, że nawet prosty przykład posiada hierarchię kontenerów o wielu poziomach. Korzeniem hierarchii kontenerów jest zawsze kontener typu top-level.
Każdy kontener typu top-level zawiera (czasami również pośrednio) kontener pośredni lub pomocniczy, określany jako content-pane. W większości programów nie ma znaczenia, czy i jakie kontenery znajdują się faktycznie pomiędzy kontenerem typu top-level a jego content-pane. Zasadniczo content-pane zawiera (bezpośrednio lub pośrednio) wszystkie widoczne elementy okna. Wyjątek stanowi okno aplikacji, które obejmuje dodatkowe obszary (CoolBar, StatusBar, Navigator, …).
Aby dodać komponent do kontenera, należy użyć jednej z dostępnych metod add. Metoda add wymaga co najmniej jednego argumentu (elementu, który ma zostać dodany). Czasami konieczne jest podanie dodatkowego argumentu w celu określenia szczegółowych informacji dotyczących układu. W poniższym przykładzie kodu wykorzystano to na przykład do umieszczenia kontenera View w centralnej części kontenera (content-pane).
Za pomocą poniższego kodu można utworzyć przedstawiony powyżej dialog:
dialog = new Dialog(…);
button = new Button(…);
label = new Label(…);
view = new View(…);
view.add(button);
view.add(label);
dialog.getContentPane().add(view, …);
Zarządzanie układem
Obliczanie rozmiaru i położenia elementów wewnątrz kontenera określane jest jako zarządzanie układem (Layout-Management). Każdy kontener posiada menedżera układu. Jest to obiekt, który przeprowadza obliczenia układu dla elementów znajdujących się w danym kontenerze.
Elementy przekazują menedżerowi układu informacje dotyczące wymaganego przez nie rozmiaru lub wyrównania. Na podstawie przyjętej strategii układu menedżer układu decyduje, czy informacje te zostaną uwzględnione.
Środowisko UI udostępnia pięciu menedżerów układu (więcej informacji znajduje się w rozdziale Menedżery układu):
- BorderLayout
- BoxLayout
- FlowLayout
- StandardLayout
- XYLayout
Pierwsze trzy układy zachowują się analogicznie do menedżerów układu o tych samych nazwach w AWT lub Swing.
StandardLayout jest najczęściej używanym menedżerem układu w systemie Comarch ERP Enterprise i oferuje specjalne wsparcie dla układów wielokolumnowych z polami oraz dla linii wyrównania (Guides) przebiegających przez kontenery.
XYLayout umożliwia pikselowo dokładne pozycjonowanie elementów, jednak powinien być stosowany wyłącznie wtedy, gdy mają być rozmieszczane elementy niezależne od czcionek (ich rozmiarów) i/lub rozdzielczości ekranu.
Za każdym razem, gdy element jest wstawiany do kontenera za pomocą add, należy uwzględnić menedżera układu ustawionego dla tego kontenera. Menedżery układu (BorderLayout, StandardLayout, XYLayout) wymagają dodatkowych informacji (określanych również jako constraints) na potrzeby pozycjonowania w kontenerze. Informacje te należy przekazać jako dodatkowy parametr metody add. Pozostałe menedżery układu (BoxLayout, FlowLayout) rozmieszczają elementy po prostu w kolejności, w jakiej zostały dodane za pomocą add.
Ustawienia menedżera układu
W odróżnieniu od AWT lub Swing, dla kontenerów środowiska UI zawsze musi być ustawiony menedżer układu. Pozycjonowanie absolutne setLayout(null) nie jest obsługiwane. Można je obejść za pomocą XYLayout, jednak zasadniczo nie jest to zalecane, ponieważ takie układy nie dostosowują się przy zmianie rozmiaru okna, a ponadto są zależne od określonych rozdzielczości ekranu i/lub czcionek.
Ustawienie menedżera układu powinno następować zawsze bezpośrednio podczas tworzenia kontenera, w szczególności przed dodaniem do kontenera elementów.
Menedżer układu można zmienić również później, jednak w takim przypadku należy najpierw usunąć wszystkie elementy, a następnie dodać je ponownie.
Poniższy fragment kodu pokazuje, jak można ustawić BorderLayout dla kontenera:
View view = new View();
view.setLayout(new BorderLayout());
Wskazówki dla menedżera układu
Elementy mogą przekazywać menedżerowi układu wskazówki lub wymagania dotyczące ich rozmiaru albo wyrównania. W tym celu wszystkie VisualElements posiadają następujące właściwości:
- preferredWidth, preferredHeight
- minimumWidth, minimumHeight
- maximumWidth, maximumHeight
- alignmentX oraz alignmentY
Różne menedżery układu uwzględniają te właściwości w zależności od swojej strategii: w pełni, częściowo albo wcale.
Wartości standardowe wymienionych właściwości są obliczane samodzielnie przez każdy element na podstawie jego typu i/lub zawartości. Programista ma możliwość nadpisania każdej z tych właściwości własnymi wartościami, np. za pomocą setMaximumWidth(). Nadpisywanie metod typu getter nie jest jednak możliwe lub nie prowadzi do oczekiwanego rezultatu.
Odstępy
Istnieją różne możliwości rozmieszczania elementów nie bezpośrednio obok siebie, lecz z zachowaniem odstępów:
- Niektóre menedżery układu wspierają to bezpośrednio, np. poprzez odpowiednie właściwości (np. hgap, vgap)
- Wstawianie niewidocznych elementów. W szczególności do zastosowania z BoxLayout dostępne są elementy Glue oraz Strut.
- Stosowanie Margins. Pozwala to określić dla elementu lub kontenera, jak duży ma być odstęp pomiędzy jego krawędzią a zawartością
- Stosowanie EmptyBorder. Odstęp od krawędzi wynika w tym przypadku z grubości niewidocznej ramki. Efekt jest taki sam jak przy użyciu Margins, jednak ze względu na przejrzystość zaleca się stosowanie Margins.
VisualElement
Klasa com.cisag.pgm.gui.VisualElement jest klasą bazową dla wszystkich komponentów wizualnych środowiska GUI. Definiuje podstawowe właściwości wszystkich elementów — podobnie jak:
- java.awt.Component dla AWT
- javax.swing.JComponent dla Swing
Do tych podstawowych właściwości należą na przykład:
- identyfikacja
- właściwości prezentacji
- stany (enabled, editable, visible)
- wsparcie (wskazówki) dla menedżerów układu
- wsparcie dla sterowania klawiaturą
- wsparcie dla metadanych
- obsługa zdarzeń
- ToolTips, pomoc bezpośrednia oraz menu kontekstowe
Identyfikacja
Każdemu VisualElement podczas instancjowania należy przypisać identyfikator GUID. Za pomocą tego identyfikatora można w dowolnym momencie zidentyfikować dany element. Jest to konieczne, jeśli później dla elementu mają zostać zdefiniowane specjalne uprawnienia. Istotne jest, aby z jednej strony rozróżniane VisualElements faktycznie miały przypisane różne identyfikatory GUID, a z drugiej strony — aby identyfikator GUID dla elementów, które mają być identyfikowane, zawsze pozostawał stały.
Jeśli aplikacja instancjuje pole Numer artykułu, wówczas temu polu przypisywany jest identyfikator GUID. Każda instancja tej aplikacji posiada własną instancję pola Numer artykułu, ale instancje te mają ten sam identyfikator GUID. Jeśli w tej samej aplikacji instancjowane jest drugie pole, np. Cena, wówczas musi ono mieć inny identyfikator GUID niż pole Numer artykułu.
Jeżeli natomiast inna aplikacja również wykorzystuje pole Numer artykułu, to z reguły należy założyć, że jest to inne pole — dlatego identyfikatory GUID muszą być różne.
Określenie identyfikatora GUID jest możliwe wyłącznie w konstruktorze danego elementu, tzn. w klasach dziedziczących po VisualElement, i nie może zostać później zmienione. W aktualnej wersji Comarch ERP Enterprise niektóre podklasy (np. Button) nie wspierają jeszcze podawania identyfikatorów GUID.
Aby uprościć nadawanie identyfikatorów GUID, środowisko deweloperskie wspiera wartość com.cisag.pgm.datatype.Guid.AUTOGUID. Tę stałą można stosować w miejscach, w których (jednorazowo) ma zostać nadany identyfikator GUID. Podczas weryfikacji automatycznie wyliczany jest nowy identyfikator GUID, a tekst com.cisag.pgm.datatype.Guid.AUTOGUID zostaje nim zastąpiony.
TextField field =
new TextField(Guid.AUTOGUID, …);
przykładowo zostaje zastąpione przez:
TextField field = new TextField(
„01E09C573EC51E10AC1C8464641F0000”, …);
Metadane
Podczas tworzenia aplikacji dla Comarch ERP Enterprise metadane odgrywają ważną rolę. Ma to również silny wpływ na sposób programowania interfejsu GUI aplikacji Comarch ERP Enterprise. Metadane (np. dla atrybutów lub logicznych typów danych) są utrzymywane w repozytorium, dlatego należy unikać redundancji w kodzie GUI. W przeciwnym razie każda zmiana wymagałaby wielokrotnej aktualizacji, co częściowo niwelowałoby sens stosowania metadanych.
Środowisko UI wspiera tworzenie elementów GUI na podstawie metadanych. Aby na przykład skonfigurować pole tekstowe tak, aby przedstawiało określony atrybut obiektu biznesowego, wystarczy w konstruktorze podać (w pełni kwalifikowaną) nazwę atrybutu. Dzięki temu pole tekstowe automatycznie otrzymuje właściwą etykietę (Label), podpowiedź (ToolTip), pomoc bezpośrednią, a w razie potrzeby również funkcję wyszukiwania i/lub menu kontekstowe.
Podobne wsparcie dostępne jest także dla przycisków oraz innych elementów. W przypadku przycisków lub wpisów menu nie są jednak referencjonowane metadane atrybutu, lecz metadane Action. Programista nie musi zajmować się lokalizacją tekstów — zadanie to jest automatycznie realizowane przez środowisko.
Zasadniczo podczas instancjowania elementów GUI w konstruktorze należy przekazać ścieżkę. Ścieżka ta określa obiekty deweloperskie z repozytorium, których dane mają zostać wykorzystane do inicjalizacji elementu GUI. W zależności od rodzaju elementu GUI mogą być stosowane różne obiekty deweloperskie. Poniżej znajduje się (niepełny) przegląd:
- Fields — atrybut, logiczny typ danych, DataDescription
- Actions, Buttons — Action
- Icon — Icon
- Label, GroupBox, … — StringTable
W połączeniu z kontenerami możliwe jest również stosowanie ścieżek względnych, tzn. dla kontenera określany jest pewien ścieżka bazowa, a dla znajdujących się w nim pól podawana jest jedynie pozostała część ścieżki.
W artykule Podręcznik programowania bardziej szczegółowo omówiono logiczne typy danych, natomiast w artykule Opis danych opisano, jakie właściwości UI mogą być definiowane w repozytorium.
Środowisko UI wykorzystuje do reprezentacji metadanych następujące klasy:
- com.cisag.pgm.dialog.model.DataDescription
- com.cisag.pgm.dialog.model.ActionDescription
Odpowiednie instancje są tworzone automatycznie na podstawie ścieżki. Istnieje również możliwość tworzenia własnych instancji DataDescription i wykorzystywania ich do inicjalizacji pól.
Obsługa zdarzeń (Actions)
Obsługa zdarzeń w com.cisag.pgm.gui różni się nieco od modelu w AWT/Swing:
- Zamiast wielu różnych typów zdarzeń i listenerów istnieją tylko klasy com.cisag.pgm.gui.Action jako zdarzenie oraz com.cisag.pgm.gui.ActionListener jako listener zdarzeń
- Podobnie jak w javax.swing.Action, instancje klasy com.cisag.pgm.gui.Action posiadają stan (np. enabled) oraz informacje do wizualizacji (tekst, ikony, podpowiedzi ToolTip). W przeciwieństwie do javax.swing.Action funkcjonalność ta nie jest przypisana do listenera, lecz do samego zdarzenia.
- Wszystkie zdarzenia posiadają identyfikator (id) typu int, co ułatwia centralną obsługę zdarzeń przy użyciu instrukcji switch
- Niektóre elementy (np. przyciski) mogą być tworzone wyłącznie poprzez Actions
Look & Feel
Podobnie jak Swing, Comarch ERP Enterprise wspiera wymienne Look & Feel, tzn. wygląd i zachowanie interfejsu (GUI) mogą być w pewnych granicach zmieniane lub dostosowywane do własnych potrzeb (gustu). Aktualna wersja Comarch ERP Enterprise umożliwia użytkownikowi np. wybór pomiędzy różnymi tematami (Themes), przy czym obecne tematy różnią się jedynie zestawieniem kolorów. Ponadto użytkownik może wpływać na wielkość czcionki (lub rozdzielczość).
Niezależnie od możliwości dostosowania interfejsu przez użytkownika, wszystkie aplikacje w Comarch ERP Enterprise powinny mieć możliwie jednolity wygląd i zachowanie. Z tego powodu zdefiniowano odpowiednie style guide dla aplikacji Comarch ERP Enterprise.
Obszar UI w Comarch ERP Enterprise wspiera te wymagania, ponieważ właściwości elementów istotne dla Look & Feel są w miarę możliwości automatycznie (ew. dynamicznie) ustalane lub wstępnie ustawiane.
Jawne ustawianie lub zmienianie tych właściwości (np. koloru, odstępów, ramek, czcionki itd.) jest zazwyczaj niepotrzebne, a nawet może być szkodliwe, jeśli stoi w sprzeczności ze style guide lub ustawieniami użytkownika.
W przypadkach, które nie są wyraźnie uwzględnione w obszarze UI lub w style guide, programista może poprzez klasę com.cisag.pgm.gui.UIManager uzyskać pośredni dostęp, tj. za pomocą nazw symbolicznych, do puli standardowych kolorów, odstępów i czcionek.
W ten sposób pozostaje zagwarantowane, że dostosowania użytkownika nadal będą uwzględniane.
Sterowanie klawiaturą
Aby optymalnie wspierać użytkowników Comarch ERP Enterprise w obsłudze, wszystkie funkcje powinny być w miarę możliwości dostępne również bez myszy, poprzez sterowanie klawiaturą. Szczególnie w przypadku powtarzalnych czynności (np. wprowadzanie danych) spójne sterowanie klawiaturą sprzyja efektywnej pracy. Dalsze wskazówki na ten temat znajdują się w artykule Comarch ERP Enterprise Style Guide.
Obszar UI wspiera sterowanie klawiaturą na różnych poziomach:
- sterowanie fokusem
- klawisze dostępu (Access Keys)
- skróty (Shortcuts)
Sterowanie fokusem
Fokus klawiatury określa aktywny element UI, na który wpłynie następne wprowadzenie z klawiatury przez użytkownika. Oprócz kliknięcia myszą użytkownik może zmieniać element z fokusem również za pomocą klawiatury. Sterowanie fokusem stanowi zatem alternatywną możliwość nawigacji.
Obszar UI wykorzystuje menedżera fokusu, który steruje i kontroluje zmianę fokusu przy użyciu klawiatury. Jego podstawową funkcją jest umożliwienie nawigacji pomiędzy elementami interfejsu przy użyciu klawiatury. W tym celu menedżer fokusu – zgodnie z powszechną praktyką – wykorzystuje klawisz [TAB]:
- Za pomocą [TAB] przechodzi się z jednego elementu do następnego elementu
- Za pomocą [SHIFT]+[TAB] przechodzi się do poprzedniego elementu
Kolejność [TAB] elementów jest przy tym zazwyczaj ustalana automatycznie przez menedżera fokusu. Wyprowadza on tę kolejność zasadniczo na podstawie rozmieszczenia elementów na ekranie, poruszając się wierszami od lewej do prawej oraz z góry na dół. Elementy niewidoczne, nieedytowalne lub disabled są automatycznie wyłączane (pomijane). Programista ma jednak możliwość wpływania na to zachowanie:
- Za pomocą setFocusTraversable(boolean b) można określić dla danego elementu, czy ma on być w ogóle osiągalny przy użyciu [TAB] lub [SHIFT]+[TAB]. Przy wartości false dany element jest pomijany przy użyciu [TAB], ale z reguły nadal może zostać wybrany za pomocą myszy.
Menedżer fokusu uwzględnia również właściwość elementu focusCycleRoot. W przypadku elementów posiadających podelementy (kontenery, tabele) właściwość ta powoduje, że fokus klawiatury nie opuszcza danego elementu, lecz krąży pomiędzy jego podelementami. Opuszczenie takiego elementu jest wówczas możliwe tylko za pomocą [CTRL]+[G] lub [CTRL]+[SHIFT]+[G].
W aktualnej wersji Comarch ERP Enterprise właściwość tę można wprawdzie sprawdzić przy użyciu isFocusCycleRoot(), jednak nie można jej zmieniać.
Access Keys (Mnemonics)
Dla wszystkich elementów posiadających etykietę (pola, przyciski, pozycje menu) można zdefiniować Access Key (mnemonik). Jest to klawisz alfanumeryczny, który użyty w połączeniu z klawiszem [ALT] powoduje przejście do danego elementu interfejsu oraz jego aktywację lub ustawienie fokusu wejściowego.
Definicja Access Key następuje poprzez wstawienie znaku & przed wybranym znakiem w tekście etykiety. Aby zasygnalizować użytkownikowi, czy i za pomocą którego klucz dostępu element jest dostępny lub możliwy do aktywacji, odpowiedni znak w tekście etykiety jest wyróżniany podkreśleniem.
Zazwyczaj etykiety elementów są definiowane w repozytorium (DataDescription lub Action). W odpowiednim obiekcie deweloperskim znak & należy po prostu wprowadzić w polu Label.
Shortcuts (Accelerators)
Shortcuts (Accelerators) to klawisze lub kombinacje klawiszy, które umożliwiają użytkownikom szybki dostęp do często używanych funkcji.
[CTRL] + klawisz oraz klawisze funkcyjne ([F1] do [F12]) są z reguły najlepiej przystosowane jako skróty. Wskazówki dotyczące prawidłowego stosowania skrótów znajdują się w artykule Comarch ERP Enterprise Styleguide. Przegląd już używanych/przypisanych skrótów znajduje się w artykule Podręcznik obsługi.
Definicja (rejestracja) skrótów odbywa się przy użyciu metod registerKeyStroke(Action action) lub registerKeyStroke(String keyStroke, Action action) w klasie com.cisag.pgm.gui.VisualElement.
W metodzie registerKeyStroke(Action action) kombinacja klawiszy (KeyStroke) jest pobierana z metadanych Action (repozytorium). Natomiast w metodzie registerKeyStroke(String keyStroke, Action action) jest ona określana bezpośrednio przez programistę.
Przy specyfikacji keyStroke w przypadku znaków drukowalnych (ASCII 33 do 127) należy użyć bezpośrednio odpowiedniego znaku, a w przypadku znaków niedrukowalnych lub klawiszy funkcyjnych – angielskiej nazwy (wielkimi literami). Przykłady:
- [a]
- [ENTER]
- [SPACE]
- [F2]
Jeśli zamiast pojedynczego klawisza ma zostać zdefiniowana kombinacja klawiszy, można to wyrazić np. jako:
- [CTRL]+[F2]
- [ALT]+[SHIFT]+[F10]
- [ALT]+[SHIFT]+[a]
Element, w którym zarejestrowano skrót przy użyciu registerKeyStroke, stanowi jednocześnie kontekst, w którym ten skrót działa. Fokus klawiatury musi znajdować się w tym elemencie lub (w przypadku kontenerów) w jednym z jego podelementów, aby zarejestrowana Action została wykonana po użyciu skrótu.
Możliwe jest przypisanie dwóch różnych Actions do tego samego skrótu, jeśli rejestracja następuje w różnych elementach. To, czy i która Action zostanie wykonana, zależy od tego, czy i który element posiadał fokus klawiatury.
Ręczna rejestracja skrótów jest rzadko konieczna, ponieważ większość odpowiednich funkcji (Actions) jest rejestrowana automatycznie. Dotyczy to np. wszystkich Actions, które są wstawiane jako przycisk lub SmartButton w MainCoolBar. W takich przypadkach MainCoolBar przejmuje rejestrację skrótu zdefiniowanego w repozytorium. Podobnie działają paski narzędzi list i tabel.
Komunikaty
Dla aplikacji Comarch ERP Enterprise obowiązuje zasada, że dialogi (w szczególności modalne) powinny być stosowane wyłącznie wtedy, gdy rzeczywiście konieczne jest, aby użytkownik w danym momencie podjął decyzję. Na przykład wtedy, gdy chce załadować nowy obiekt, ale zmienione dane poprzedniego obiektu nie zostały jeszcze zapisane.
Zasadniczo użytkownik powinien sam decydować, w jakiej kolejności wprowadza dane. Dotyczy to również sytuacji, w których system stwierdzi, że niektóre wprowadzone dane są niepoprawne/nieważne. Użytkownik nie powinien w żadnym wypadku być uwięziony w polu zawierającym błąd, dopóki nie wprowadzi poprawnej wartości. Nie powinien także być ograniczany w płynnym wprowadzaniu danych przez natarczywe dialogi, Ważne wskazówki na ten temat znajdują się również w artykule Comarch ERP Enterprise Styleguide).
W Comarch ERP Enterprise komunikaty są wyświetlane na specjalnej stronie komunikatów (w nagłówku) i/lub w pasku statusu. W przypadku komunikatów odnoszących się do pól dodatkowo oznaczane jest odpowiednie pole (czerwony róg).
W przypadku komunikatów (powiązanych z polem) rozróżnia się dwa rodzaje:
- komunikaty generowane przez system w wyniku (generycznych) kontroli
- komunikaty tworzone przez programistę aplikacji w zaimplementowanych przez niego kontrolach
System przeprowadza kontrole na podstawie metadanych. Są to głównie kontrole typu, składni oraz długości. Jeżeli np. w DataDescription określono, że dopuszczalne jest maksymalnie 10 znaków, dane pole zostanie automatycznie oznaczone czerwonym rogiem, jeśli jego zawartość przekroczy ten limit. Tego rodzaju kontrole odbywają się z reguły już przy opuszczaniu pola, najpóźniej podczas kolejnej komunikacji z serwerem.
Komunikaty specyficzne dla aplikacji opierają się na odpowiednich kontrolach w logice aplikacji lub w specjalnych klasach walidacyjnych (Validations). W odróżnieniu od kontroli generycznych wykonywanych przez system, kontrole te są przeprowadzane tylko przy określonych akcjach użytkownika, np. [Zapisz] lub [Weryfikuj].
Kolejna istotna różnica polega na tym, że kontrole te oraz powiązane z nimi komunikaty muszą zostać jawnie przypisane do odpowiednich pól, ponieważ klasy walidacyjne muszą być zasadniczo niezależne od UI. Artykuł Podręcznik programowania zawiera w rozdziale Komunikaty szczegółowy opis rejestrowania i wysyłania komunikatów wraz z przykładami kodu.
Aplikacje dialogowe
W Comarch ERP Enterprise istnieją różne rodzaje aplikacji. Aplikacje z graficznym interfejsem użytkownika (GUI) są określane jako aplikacje dialogowe. Innym typem aplikacji są na przykład aplikacje działające w tle (aplikacje wsadowe), które nie posiadają GUI.
W Comarch ERP Enterprise główne okno aplikacji jest udostępniane przez system. System, czyli aplikacja ramowa (JobControl), odpowiada również za nawigację między aplikacjami. Aby różne aplikacje dialogowe mogły działać równolegle w Comarch ERP Enterprise, muszą spełniać określone wymagania oraz przestrzegać standardów.
Dla aplikacji dialogowych w Comarch ERP Enterprise obowiązuje zatem zasada, że muszą one dziedziczyć po abstrakcyjnej klasie bazowej com.cisag.pgm.base.CisUiApplication.
Ta klasa nadrzędna z jednej strony zwalnia programistę aplikacji z wykonywania standardowych zadań, z drugiej zaś zapewnia dostęp do zdefiniowanych obszarów w głównym oknie aplikacji (Navigator/Locator, CoolBar, IdentArea oraz WorkArea).
Aplikacja dialogowa powinna standardowo implementować co najmniej trzy następujące metody:
- Metoda init() – wywoływana przez system w celu inicjalizacji aplikacji
- Metoda performAction() – służy do obsługi występujących zdarzeń
- Metoda run() – umożliwia przekazanie do aplikacji parametrów startowych
Inicjalizacja GUI odbywa się zazwyczaj w metodzie init(), tzn. w tej metodzie tworzone są niezbędne elementy oraz budowana jest hierarchia kontenerów. Klasa nadrzędna CisUiApplication udostępnia poprzez metody getIdentPane(), getWorkPane() oraz getMainCoolBar() kontenery, do których można wstawiać własne elementy.
Elementy specjalne
Pakiet com.cisag.pgm.gui zawiera szereg specjalnych elementów, które mają wspierać programistę aplikacji w łatwiejszej realizacji standardowych zadań zgodnie ze style guide. Należą do nich m.in. listy, tabele oraz EntityFields.
Listy i tabele
W Comarch ERP Enterprise rozróżnia się listy i tabele. Tabele są z reguły stosowane wtedy, gdy kolumny i wiersze tworzą jednoznaczną strukturę siatki. Listy natomiast mogą być używane również wtedy, gdy każdy wiersz zawiera inne dane lub gdy dane muszą być prezentowane w wielu wierszach.
Zarówno listy, jak i tabele uwzględniają fakt, że w aplikacji ERP nie wszystkie dane mogą być wyświetlane jednocześnie. Za pomocą tzw. modelu stron (PageModel) użytkownik może przewijać dane, a programista może dynamicznie doładowywać dane przeznaczone do wyświetlenia.
Model danych list i tabel opiera się na liście rekordów danych. Taki rekord jest reprezentowany przez instancję klasy com.cisag.pgm.base.obj.CisListPartMutable.
Obiekt ten pełni funkcję generycznego kontenera danych, tzn. może przechowywać referencję do dowolnego obiektu (biznesowego), jak również dodatkowe informacje (status) dotyczące danego rekordu. Przykładami takich informacji są zaznaczenie, oznaczenie do usunięcia lub zmiany oraz wskazówki dotyczące wizualizacji.
Podczas gdy model danych zawsze zawiera wszystkie załadowane rekordy, wyświetlanie na ekranie jest ograniczone do jednej strony. Wielkość strony, czyli liczba rekordów na stronę, może być określona przez programistę i powinna być zawsze dobrana w taki sposób, aby lista/tabela była w pełni wypełniona i nie było potrzeby stosowania pasków przewijania.
Wizualizacja rekordów należących do danej strony odbywa się w obu elementach wierszami. Właściwa wizualizacja, czyli mapowanie danych (atrybutów) na elementy UI, jest delegowane do obiektów zewnętrznych. W przypadku list są to ListViews, a w przypadku tabel TableDataManager.
Obiekty te odpowiadają również za (również wierszowe) odwrotne mapowanie, czyli przenoszenie wprowadzonych przez użytkownika danych z elementów UI do rekordów danych.
W artykule Podręcznik programowania działanie i zastosowanie list oraz tabel zostało szczegółowo opisane. Odpowiednie aplikacje przykładowe są udostępniane przez klasy com.cisag.app.edu.ui.BookListInquiry oraz com.cisag.app.edu.ui.BookTableMaintenance.
Artykuł Comarch ERP Enterprise Styleguide zawiera wytyczne dla określonych przypadków użycia (wyszukiwanie, tylko podgląd, z możliwością/bez możliwości wprowadzania danych itd.), które należy uwzględnić przy stosowaniu list i tabel.
EntityFields
EntityFields to pola specjalne służące do identyfikacji encji biznesowych (Business Entities). Umożliwiają one nie tylko wprowadzanie jednoznacznej identyfikacji encji biznesowej (atrybutów kluczowych), lecz także wyświetlanie opisu danej encji. Ponadto EntityFields udostępniają szereg dodatkowych funkcji dla wyświetlanej encji:
- pomoc przy wprowadzaniu wartości
- linki do przypisanych aplikacji
- menu kontekstowe
- wsparcie dla Drag & Drop
Pakiet com.cisag.pgm.gui udostępnia tę funkcjonalność w postaci abstrakcyjnych klas bazowych: com.cisag.pgm.gui.EntityField oraz com.cisag.pgm.gui.SimpleEntityField.
Dla każdej encji należy przygotować własną, dostosowaną implementację, przy czym – w zależności od złożoności encji – jako klasę nadrzędną należy zastosować albo com.cisag.pgm.gui.EntityField albo com.cisag.pgm.gui.SimpleEntityField.
Dalsze informacje znajdują się w artykule Podręcznik programowania w rozdziale EntityFelder.
Rozszerzanie i dostosowywanie elementów GUI
Zasadniczo możliwe jest rozszerzanie klas z com.cisag.pgm.gui poprzez dziedziczenie. Jeśli to możliwe, istniejące klasy powinny być konfigurowane za pomocą dostępnych metod.
Przy nadpisywaniu metod należy pamiętać o następujących zasadach:
- Oryginalna implementacja musi zostać wywołana przy użyciu super
- Nadpisywanie niektórych getterów (np. getPreferredSize()) działa tylko w ograniczonym zakresie, ponieważ implementacja (com.cisag.pgm.dialog.) nie korzysta z tych metod
Powiązanie z com.cisag.pgm.dialog
Praktycznie wszystkie klasy pochodne od com.cisag.pgm.gui.VisualElement delegują właściwą implementację do odpowiedniej klasy z pakietu com.cisag.pgm.dialog. Wewnętrznie powiązana instancja jest zazwyczaj reprezentowana przez (protected) zmienną o nazwie x.
Należy zwrócić uwagę, że inicjalizacja zmiennej x nie następuje we wszystkich elementach już w konstruktorze, lecz częściowo dopiero przy dodaniu (add) do kontenera. Do tego typu elementów należą na przykład wszystkie klasy dziedziczące po com.cisag.pgm.gui.Field.
Zachowanie to wynika z faktu, że metadane niezbędne do utworzenia elementów x mogą zostać pobrane dopiero wtedy, gdy ścieżka do metadanych jest w pełni znana. W przypadku ścieżek względnych pełne obliczenie ścieżki jest jednak możliwe dopiero poprzez kontener.
Jeżeli metoda zostanie wywołana na elemencie przed zainicjalizowaniem elementu x, może dojść do wystąpienia java.lang.NullPointerException.
Z zewnątrz można uzyskać dostęp do elementu x poprzez klasę pomocniczą com.cisag.pgm.gui.WebInterface. Zasadniczo zdecydowanie odradza się jednak korzystanie z tej możliwości, ponieważ ta właściwość nie należy do publicznego interfejsu klas w com.cisag.pgm.gui i może w każdej chwili ulec zmianie lub całkowicie zniknąć.
Jeśli w ogóle, użycie com.cisag.pgm.gui.WebInterface powinno być traktowane jako ostateczność. Wspomniano o niej tutaj jedynie dlatego, że może być jeszcze stosowana w niektórych (starych) aplikacjach.
Referencja
Klasy bazowe
VisualElement
W (abstrakcyjnej) klasie bazowej com.cisag.pgm.gui.VisualElement zebrano wspólne metody i właściwości wszystkich (wizualnych) elementów z pakietu com.cisag.pgm.gui. Wszystkie elementy, włącznie z różnymi rodzajami kontenerów, dziedziczą po tej klasie.
Najważniejsze właściwości i zadania VisualElement zostały opisane w rozdziale VisualElement.
Implementacja
Znaczna część właściwej implementacji obszaru UI nie znajduje się w pakiecie com.cisag.pgm.gui, lecz w pakiecie com.cisag.pgm.dialog.
Każdej instancji com.cisag.pgm.gui.VisualElement jest w czasie wykonywania przypisana (co najmniej) jedna instancja com.cisag.pgm.dialog.VisualElement.
Referencję do instancji com.cisag.pgm.dialog.VisualElement element com.cisag.pgm.gui.VisualElement przechowuje w zmiennej instancyjnej o nazwie x. Dlatego w tym kontekście często mówi się również o elemencie X.
Tworzenie (instancjonowanie) elementu X odbywa się poprzez konkretne elementy (Button, TextField. itd.), przy czym moment utworzenia zależy od typu elementu. W przypadku niektórych elementów powiązany element X jest tworzony już w konstruktorze, natomiast w przypadku innych (w szczególności pól) dopiero przy dodaniu (add) do kontenera.
Z tego powodu elementy powinny być zawsze najpierw dodawane do (właściwego) kontenera, zanim zostaną na nich wywołane jakiekolwiek metody. W przeciwnym razie może wystąpić java.lang.NullPointerException.
Relacja elementu do elementu X występuje również we wszystkich klasach pochodnych. Na przykład elementowi com.cisag.pgm.gui.Button jest w czasie wykonywania przypisany com.cisag.pgm.dialog.Button.
Przy czym com.cisag.pgm.gui.Button (oprócz referencji o tej samej nazwie w com.cisag.pgm.gui.VisualElement) posiada również własną zmienną instancyjną o nazwie x. Obie referencje wskazują w czasie wykonywania na tę samą instancję com.cisag.pgm.dialog.Button.
Konstrukcja ta służy temu, aby w klasach pochodnych można było uzyskiwać dostęp do metod specyficznych dla danej klasy bez konieczności stosowania operacji rzutowania (cast).
VisualElement API
Poniższe tabele przedstawiają najważniejsze i najczęściej używane metody VisualElement (pełna lista oraz szczegółowe opisy znajdują się w artykule JavaDoc dla VisualElement):
Tabela 1: Wygląd wizualny
| Metoda | Zastosowanie |
| getBorder() setBorder(Border) |
Odczyt, ustawienie lub zmiana ramki (Border). Więcej informacji znajduje się w rozdziale Border. |
| getMargin() setMargin(Insets) setMargin(int, int, int, int) getInsets() |
Odczyt, ustawienie lub zmiana odstępów (Margins/Insets). Więcej informacji znajduje się w rozdziale Insets. |
| getForeground() setForeground(Color) getBackgraound() getBackgraound(Color) |
Odczyt, ustawienie lub zmiana koloru pierwszego planu (czcionki) lub tła. Więcej informacji znajduje się w rozdziałach Font oraz UIManager. |
| getFont() setFont(Font) |
Odczyt, ustawienie lub zmiana rodzaju czcionki, jej rozmiaru i stylu. Więcej informacji znajduje się w rozdziałach Font oraz UIManager. |
| setTexture(Icon) | Ustawienie ikony jako wzoru tła (tekstury). |
Tabela 2: Stan ogólny
| Metoda | Zastosowanie |
| isEnabled() setEnabled(boolean) |
Odczyt lub zmiana stanu interaktywności (enabled/disabled). Element z enabled = true reaguje na akcje użytkownika, element z enabled = false nie. |
| isVisible() setVisible(boolean) |
Odczyt lub zmiana widoczności. Niewidoczne elementy nie są wyświetlane po stronie klienta. W zależności od menedżera układu (Layout-Manager) miejsce na ekranie zostaje zwolnione lub zajęte przez inne elementy. |
| isEditable() setEditable(boolean) |
Odczyt lub zmiana możliwości edycji. W przypadku pól wejściowych właściwość ta określa np., czy wyświetlany tekst może być zmieniany (edytowany) przez użytkownika. |
Tabela 3: Obsługa metadanych
| Metoda | Zastosowanie |
| getAbsolutePath() | Zwraca absolutną ścieżkę powiązanych metadanych (atrybut, logiczny typ danych, …). |
| getRelativePath() | Zwraca tylną część ścieżki metadanych (nazwa atrybutu). |
| getReferencePath() | Zwraca przednią część ścieżki metadanych (namespace, obiekt biznesowy). |
| setPath() | Ustawia lub oblicza względną i absolutną ścieżkę dla elementu. Metoda jest wywoływana przez obszar UI – albo już w konstruktorze, albo po dodaniu elementu do kontenera (addedToContainer()). |
| getToolTipText() setToolTipText(String) setToolTipTextFrom(String) setToolTipTextFrom(String, Object[]) |
Odczyt, ustawienie lub zmiana tekstu podpowiedzi (ToolTip). Określony tekst jest wyświetlany, gdy użytkownik najeżdża kursorem myszy na element. W przypadku elementów tworzonych na podstawie metadanych (DataDescription, ActionDescription) wartość ta jest ustawiana automatycznie. |
| getContextHelpID() setContextHelpID(String) |
Odczyt, ustawienie lub zmiana identyfikatora pomocy kontekstowej ([F1]). W przypadku elementów tworzonych na podstawie metadanych (DataDescription, ActionDescription) wartość ta jest ustawiana automatycznie. |
| isRequired() setRequired(boolean) |
Sprawdzenie lub określenie, czy dany element jest tzw. polem obowiązkowym. W przypadku elementów tworzonych na podstawie metadanych (DataDescription) wartość ta jest ustawiana automatycznie. |
Tabela 4: Obsługa zdarzeń
| Metoda | Zastosowanie |
| getDoubleClickAction() setDoubleClickAction(Action) |
Odczyt aktualnej akcji podwójnego kliknięcia lub rejestracja nowej akcji. Zarejestrowana akcja jest wykonywana, gdy użytkownik wykona podwójne kliknięcie na elemencie. |
| getLinkAction() setLinkAction(Action) |
Odczyt aktualnej akcji linku lub rejestracja nowej akcji. Zarejestrowana akcja jest wykonywana, gdy użytkownik kliknie element lewym przyciskiem myszy. Zazwyczaj akcja implementuje działanie podobne do hiperlinku. |
| getPopupAction() setPopupAction(Action) |
Odczyt aktualnej akcji podpowiedzi lub rejestracja nowej akcji. Zarejestrowana akcja jest wykonywana, gdy użytkownik np. kliknie element prawym przyciskiem myszy. Zazwyczaj akcja implementuje otwarcie menu podpowiedzi. Więcej informacji można znaleźć w rozdziale Menu podpowiedzi. |
| getObjectType() setObjectType(String) |
Odczyt aktualnego typu obiektu lub ustawienie nowego typu obiektu. Typ obiektu służy do klasyfikowania elementów oraz prezentowanych w nich danych. Na podstawie typu obiektu można np. określić odpowiednie menu kontekstowe lub możliwe akcje Drag & Drop. |
| registerKeyStroke(Action) registerKeystroke(Action[]) registerKeyStroke(String,Action) deregisterKeystroke(Action) deregisterKeystroke(Action[]) deregisterKeystroke(String, Action) |
Rejestrowanie lub wyrejestrowywanie akcji dla określonych klawiszy lub kombinacji klawiszy (key strokes). Zazwyczaj odbywa się to automatycznie, a kombinacje klawiszy są zapisane w repozytorium (Obiekty deweloperskie, typ Action). |
| getActionsAsArray() | Zwraca wszystkie wewnętrznie zdefiniowane (pod)akcje. |
Tabela 5: Sterowanie fokusem
| Metoda | Zastosowanie |
| hasFocus() | Sprawdza, czy bieżący element posiada fokus klawiatury. |
| requestFocus() | Za pomocą tej metody można programowo lub po stronie serwera zażądać zmiany fokusu klawiatury. |
| isFocusTraversable() setFocusTraversable(boolean) |
Sprawdza lub określa, czy bieżący element może otrzymać fokus klawiatury za pomocą klawisza [TAB], lub czy może zostać pominięty. Wskazówka Mimo ustawienia focusTraversable = false użytkownik nadal może nadać elementowi fokus klawiatury poprzez kliknięcie myszą. |
| isRequestFocusEnabled() setRequestFocusEnabled(boolean) |
Sprawdza lub określa, czy bieżący element może otrzymać fokus klawiatury poprzez requestFocus(). Wskazówka requestFocusEnabled = false uniemożliwia również nadanie elementowi fokusu klawiatury poprzez kliknięcie myszą. |
| isFocusCycleRoot() | Sprawdza, czy element jest kontenerem z cyklicznym fokusem klawiatury, tzn. przy użyciu [TAB] oraz [Shift]+[TAB] fokus klawiatury zmienia się wyłącznie w obrębie podelementów.Aby opuścić element, użytkownik musi nacisnąć [CTRL]+[G] lub [CTRL]+[SHIFT]+[G].Ta właściwość jest zaimplementowana tylko w określonych kontenerach i nie można jej zmienić. |
| focusElementWith(VisualElement,String) | Powoduje, że po naciśnięciu określonej kombinacji klawiszy klient automatycznie przenosi fokus wprowadzania na wskazany element.Za pomocą tej metody bufor klawiatury po stronie klienta może być lepiej wykorzystany, co poprawia sposób wprowadzania danych. |
Tabela 6: Komunikaty
| Metoda | Zastosowanie |
| registerMessage(int) registerMessage(int[]) registerMessage(String, int[]) |
Łączy element (pole) z jednym lub wieloma komunikatami (ID). Więcej informacji można znaleźć w rozdziale Komunikaty. |
| requiresAttention() | Sprawdza, czy dla elementu występują komunikaty o błędach lub niepotwierdzone ostrzeżenia. |
Tabela 7: Hierarchia kontenerów
| Metoda | Zastosowanie |
| getParent() | Zwraca nadrzędny element kontenera, tzn. kontener typu VisualElementContainer, do którego ten element został dodany. |
| addedToContainer(VisualElementContainer, Object) | Metoda notify lub hook. Jest wywoływana przez obszar, gdy element został dodany do kontenera. |
| getWindow() | Śledzi hierarchię kontenerów bieżącego elementu aż do kontenera najwyższego poziomu (Window) i zwraca go. |
| isAncestor(VisualElement) | Sprawdza, czy określony element jest bezpośrednim lub pośrednim kontenerem nadrzędnym (parent) bieżącego elementu. |
Tabela 8: Obsługa układu
| Metoda | Zastosowanie |
| getAlignmentX() setAlignmentX(float) getAlignmentY() setAlignmentY(float) |
Poziome i pionowe wyrównanie (względem innych elementów w kontenerze) — odczyt, ustawianie lub zmiana. Więcej informacji można znaleźć w rozdziałach Wskazówki dotyczące menedżera układu i BoxLayout. |
| getPreferredWidth() setPreferredWidth(int) getPreferredHeight() setPreferredHeight(int) |
Preferowana (optymalna) szerokość i wysokość — odczyt, ustawianie lub zmiana. Więcej informacji można znaleźć w rozdziale Wskazówki dotyczące menedżera układu.Większość elementów automatycznie oblicza preferowany rozmiar (z uwzględnieniem rozmiaru czcionki, rozdzielczości ekranu). Dotyczy to zarówno etykiety, jak i złożonego kontenera. |
| getMinimumWidth() setMinimumWidth(int) getMinimumHeight() setMinimumHeight(int) |
Minimalna szerokość i wysokość — odczyt, ustawianie lub zmiana.Stanowi wskazówkę dla menedżera układu i określa, do jakiego stopnia element może zostać zmniejszony. Więcej informacji można znaleźć w rozdziale Wskazówki dotyczące menedżera układu. |
| getMaximumWidth() setMaximumWidth(int) getMaximumHeight() setMaximumHeight(int) |
Maksymalna szerokość i wysokość — odczyt, ustawianie lub zmiana.Stanowi wskazówkę dla menedżera układu i określa, do jakiego stopnia element może zostać powiększony. Więcej informacji można znaleźć w rozdziale Wskazówki dotyczące menedżera układu. |
Tabela 9: Obsługa eksportu danych
| Metoda | Zastosowanie |
| getDataColumnSupport() setDataColumnSupport(DataColumnSupport) |
Informacje do eksportu danych — odczyt, ustawianie lub zmiana. Więcej informacji można znaleźć w rozdziale Eksport danych. |
| createDataColumnSupport() | Podklasy mogą nadpisać tę metodę, aby dostarczyć dostosowane informacje do eksportu danych. |
VisualElementContainer
Klasa com.cisag.pgm.gui.VisualElementContainer rozszerza com.cisag.pgm.gui.VisualElement o typowe właściwości kontenera, tzn. obiekt VisualElementContainer może zawierać inne obiekty typu VisualElement.
Ponieważ VisualElementContainer sam jest obiektem typu VisualElement, możliwe jest tworzenie dowolnych hierarchii elementów oraz kontenerów. Więcej informacji można znaleźć w rozdziale Hierarchia elementów i kontenerów.
VisualElementContainer jest (abstrakcyjną) klasą bazową dla wszystkich implementacji kontenerów. Należą do nich zarówno kontenery pomocnicze, np. com.cisag.pgm.gui.View, jak i kontenery najwyższego poziomu, np. com.cisag.pgm.gui.Dialog. Więcej informacji można znaleźć w rozdziale Hierarchia elementów i kontenerów.
Kontenery najwyższego poziomu nie dziedziczą jednak bezpośrednio po VisualElementContainer, lecz po com.cisag.pgm.gui.Window (klasa bazowa dla wszystkich kontenerów najwyższego poziomu). Więcej informacji można znaleźć w rozdziale Window.
Rozmieszczenie elementów kontener deleguje do przypisanego menedżera układu. Więcej informacji można znaleźć w rozdziałach Zarządzanie układem oraz Menedżerowie układu. Przypisanie menedżera układu za pomocą setLayout() powinno z reguły nastąpić przed dodaniem elementów.
VisualElementContainer API
Poniższe tabele przedstawiają najważniejsze oraz najczęściej używane metody klasy VisualElementContainer (pełna lista oraz szczegółowe opisy znajdują się w dokumentacji JavaDoc klasy VisualElementContainer):
Tabela 10: Wygląd wizualny
| Metoda | Zastosowanie |
| INSETS_NONE INSETS_TOP INSETS_BOTTOM INSETS_LEFT INSETS_RIGHT INSETS_ALL setDefaultMargin(int) |
Ustawianie standardowych odstępów brzegowych. Więcej informacji można znaleźć w rozdziale Insets. |
Tabela 11: Obsługa aplikacji
| Metoda | Zastosowanie |
| isSelectionView() setSelectionView(boolean) |
Sprawdzenie lub określenie, czy wszystkie pola dodane (od momentu ustawienia) są automatycznie przełączane w tryb wyboru. |
| getObjectReference() setObjectReference(byte[]) |
Umożliwia powiązanie między kontenerem a referencją obiektu. |
Tabela 12: Sterowanie fokusem
| Metoda | Zastosowanie |
| setDefaultFocus(VisualElement) | Określa, do którego elementu podrzędnego ma zostać przekazany fokus klawiatury, jeśli kontener otrzyma fokus klawiatury. |
Tabela 13: Hierarchia kontenerów
| Metoda | Zastosowanie |
| add(VisualElement) add(VisualElement,int) add(VisualElement,Object) add(VisualElement,Object, int) |
Dodaje do kontenera określony element.Opcjonalnie można podać pozycję (indeks). W takim przypadku nowy element jest wstawiany na wskazane miejsce. Menedżer układu może wymagać dodatkowych informacji dotyczących rozmieszczenia nowego elementu. W tym przypadku jako dodatkowy parametr należy przekazać obiekt typu Constraint. Więcej informacji można znaleźć w rozdziałach Zarządzanie układem i Menedżerowie układu. |
| remove(VisualElement) removeAll() |
Usuwa pojedynczy element lub wszystkie elementy podrzędne. |
| contains(VisualElement) | Sprawdza, czy określony element znajduje się w kontenerze lub w jednym z jego elementów podrzędnych (rekurencyjnie). |
Tabela 14: Obsługa układu
| Metoda | Zastosowanie |
| setLayout(LayoutManager) | Przypisuje kontenerowi określonego menedżera układu. Więcej informacji można znaleźć w rozdziałach Zarządzanie układem oraz Menedżerowie układu. |
| setGuides(int) setGuides(int, boolean) setGuides(double[],double[], double[],double[]) |
Definiuje linie prowadzące dla tego oraz wszystkich podrzędnych kontenerów. Linie prowadzące są wykorzystywane przez menedżer układu StandardLayout, aby wyrównywać do nich elementy.W najprostszym przypadku podawana jest jedynie liczba kolumn (wszystkie kolumny otrzymują tę samą szerokość).Możliwość definiowania linii prowadzących poprzez konstruktory klasy StandardLayout jest oznaczona jako deprecated. Opisane tutaj metody mają tę zaletę, że definicja linii prowadzących może być niezależna od użycia konkretnego StandardLayout. Dzięki temu linie prowadzące można łatwo zdefiniować tylko raz, już od najwyższego poziomu kontenera. |
| removeAllGuides() |
Usuwa wszystkie (bezpośrednio) zdefiniowane dla tego kontenera linie prowadzące.
|
Window
Klasa com.cisag.pgm.gui.Window rozszerza com.cisag.pgm.gui.VisualElementContainer o dodatkowe właściwości dla kontenerów najwyższego poziomu. Podobnie jak w przypadku VisualElement i VisualElementContainer, jest to klasa abstrakcyjna.
W przeciwieństwie do większości kontenerów klasy dziedziczące po Window podczas tworzenia instancji wymagają co najmniej jednego Subelement. Kontenery najwyższego poziomu wykorzystują ten element do własnej implementacji, dlatego elementy nie są bezpośrednio dodawane do kontenerów najwyższego poziomu za pomocą metod add/remove. Do ogólnego dostępu Window udostępnia tzw. Content-Pane, który można traktować jak zwykły kontener, tzn. można do niego dodawać i z niego usuwać elementy. Content-Pane może zostać wymieniony za pomocą setContentPane(), jednak może to prowadzić do utraty określonych ustawień domyślnych (Look & Feel, klawiatura, sterowanie fokusem).
Window API
Poniższe tabele przedstawiają najważniejsze oraz najczęściej używane metody klasy Window (pełna lista oraz szczegółowe opisy znajdują się w dokumentacji JavaDoc klasy Window):
Tabela 15: Obsługa zdarzeń
| Metoda | Zastosowanie |
| setClosedAction(Action) | Rejestruje akcję dla zdarzenia window closed. Zarejestrowana akcja zostaje wywołana w momencie zamknięcia okna. |
| addRefreshListener(RefreshListener) removeRefreshListener(RefreshListener) |
Rejestruje lub wyrejestrowuje RefreshListener do aktualizacji po stronie klienta. Aktualizacja klienta odbywa się w regularnych odstępach czasu (więcej informacji można znaleźć w rozdziale dotyczącym getRefreshInterval()). Uwaga Zastosowanie tych metod może znacząco wpłynąć na działanie komunikacji klient/serwer. |
| getRefreshInterval() setRefreshInterval(int) |
Odczyt lub zmiana interwału aktualizacji. Uwaga Domyślna wartość stanowi dobry kompromis pomiędzy częstotliwością aktualizacji a obciążeniem systemu i nie powinna być zmieniana. Zmiana tej wartości może negatywnie wpłynąć na działanie komunikacji klient/serwer. |
| isKeyStrokeSupported(String) | Sprawdza, czy określony klawisz lub kombinacja klawiszy (key stroke) jest obsługiwana przez klienta lub przez aktualny tryb klienta. |
Tabela 16: Sterowanie fokusem
| Metoda | Zastosowanie |
| getFocusOwner() | Zwraca element podrzędny, który aktualnie posiada fokus klawiatury. |
Tabela 17: Hierarchia kontenerów
| Metoda | Zastosowanie |
| getContentPane() setContentPane(View) |
Zwraca lub ustawia Content-Pane okna. |
| getOwnedWindows() | Zwraca wszystkie okna zależne od bieżącego okna. |
Field
Klasa com.cisag.pgm.gui.Field jest (abstrakcyjną) klasą bazową dla wszystkich elementów służących do wprowadzania i wyprowadzania danych. Należą do nich proste pola tekstowe, jak również pola wyboru oraz listy rozwijane.
Klasa Field rozszerza klasę nadrzędną com.cisag.pgm.gui.VisualElement o następujące właściwości:
- Label — etykieta
- Value — wartość
- DataDescription — inicjalizacja poprzez metadane
- różne wskaźniki oraz opcjonalne elementy interakcyjne
- specjalne wsparcie dla tabel
Właściwości ogólne
Label
Pole składa się zasadniczo z dwóch części. W części głównej wyświetlana jest aktualna wartość (dane) lub może ona zostać wprowadzona bądź zmieniona przez użytkownika. Druga część (Label) pełni funkcję opisu (podpisu) i służy użytkownikowi do rozpoznania znaczenia poszczególnych pól. Część ta jest jednak opcjonalna, tzn. w określonych przypadkach zastosowania (np. w tabelach i listach) etykieta może zostać pominięta.
Dane i typy danych
Pola służą do wprowadzania i wyprowadzania danych. Na ekranie dane są często przedstawiane jako tekst. W zależności od typu danych (np. tekst, data, kwota itd.) należy jednak programowo zapewnić odpowiednie przetwarzanie.
W związku z tym pola pełnią rolę przetwarzania wewnętrznych typów danych w sposób przyjazny dla użytkownika oraz ich konwersji do wewnętrznie wykorzystywanych typów danych. Z tego powodu dla każdego typu danych dostępna jest wyprowadzona klasa typu Field, która realizuje to zadanie. Przykłady:
- TextField (java.lang.String)
- CisDateField (com.cisag.pgm.datatype.CisDate)
- DecimalField (com.cisag.pgm.datatype.CisDecimal)
- BooleanField (boolean)
Każda konkretna podklasa (zgodnie z konwencją) udostępnia metody getValue() oraz setValue(), których sygnatury (typ zwracany oraz typ parametru) są oparte na typie danych danego pola. Przykładowo TextField wykorzystuje następujące sygnatury:
public String getValue()
public void setValue(String)
Pole CisDateField wykorzystuje natomiast sygnatury:
public CisDate getValue()
public void setValue(CisDate)
Na poziomie konkretnych podklas zapewniony jest tym samym bezpieczny typowo dostęp do wartości pola.
Dodatkowo klasa bazowa Field udostępnia również dynamiczny dostęp (z wykorzystaniem java.lang.Object jako typu danych) poprzez następujące metody:
Zazwyczaj podczas tworzenia instancji pól wykorzystywane są metadane pochodzące z repozytorium Comarch ERP Enterprise. W tym celu przy wywołaniu odpowiedniego konstruktora przekazywana jest ścieżka.
Ścieżka ta może być podana zarówno w postaci absolutnej (łącznie z nazwą kontenera), jak i w postaci względnej (wówczas nazwa kontenera jest wyszukiwana automatycznie). Ścieżka identyfikuje atrybut obiektu biznesowego lub wewnętrzny typ logiczny danych.
Jeżeli pole zostanie dodane do kontenera, następuje automatyczne określenie powiązanego obiektu DataDescription. Obiekt DataDescription jest reprezentowany przez instancję com.cisag.pgm.dialog.model.DataDescription i udostępnia praktycznie wszystkie metadane istotne dla pól. Należą do nich w szczególności:
- typ danych
- etykieta (łącznie z Access-Key)
- ToolTip
- pomoc bezpośrednia
- ograniczenia dotyczące zawartości (długość, zakres wartości)
- informacje o możliwości edycji oraz polach obowiązkowych
- formatowanie
- wyszukiwanie
Odpowiednie właściwości pola są dziedziczone wraz z elementem i przejmowane z wartości obiektu DataDescription. Niektóre z tych właściwości można później zmienić.
Statusy pola i gotowość do wprowadzania danych
Właściwości interakcyjne pola (enabled, editable oraz required — pola obowiązkowe) są inicjalizowane na podstawie obiektu DataDescription i mogą być w razie potrzeby zmieniane poprzez odpowiednie metody set.
Wizualizacja tych właściwości odbywa się poprzez kolory tła i/lub czcionki albo poprzez mały szary narożnik po prawej stronie pola.
Pomoc uzupełniania wartości
Pola mogą udostępniać użytkownikowi funkcję pomocy uzupełniania wartości. Funkcja ta ułatwia wybór (wyszukiwanie) lub wprowadzanie określonych wartości.
Pomoc uzupełniania wartości może być realizowana jako okno dialogowe, wyskakujące menu lub inny obiekt. W zależności od rodzaju pomocy uzupełniania wartości jej dostępność jest wizualizowana poprzez mały przycisk dialogowy (w prawym dolnym rogu) lub przycisk listy rozwijanej (ComboBox-Button).
W niektórych przypadkach funkcja pomocy uzupełniania wartości jest udostępniana bezpośrednio przez obszar interfejsu użytkownika. Przykładowo dla wszystkich pól daty dostępny jest kalendarz, a dla wszystkich pól EntityField oferowane jest wyszukiwanie w bazie danych.
Istnieje również możliwość implementacji własnych funkcji pomocy uzupełniania wartości. W takim przypadku obiekt pomocy uzupełniania wartości musi dziedziczyć po klasie com.cisag.pgm.job.search.SearchManager, Więcej informacji można znaleźć w artykule Podręcznik programowania.
Tryb wyboru
W niektórych przypadkach zastosowania, w szczególności podczas wyszukiwania oraz odczytu danych, możliwe jest wprowadzanie listy wartości, które nie są połączone znakiem [Enter], lecz separatorem (np. przecinek, spacja lub *, ?). W dokumentacji pól używanych w tym celu stosowane jest określenie wielowartościowe pola wejściowe.
Na poziomie implementacji niektóre specjalne typy pól udostępniają tę funkcjonalność (więcej informacji można znaleźć w rozdziale Tryb wyboru). Częściowo pola te muszą zostać ustawione w tryb wyboru, częściowo tryb ten jest automatycznie dostępny. Wymagana właściwość (selectionMode) jest również dostępna na poziomie klasy bazowej i jest implementowana w różny sposób w zależności od podklasy.
W szczególności pola EntityFields oraz ValueSetFields obsługują tę właściwość. Istnieją jednak także typy pól, które jej nie obsługują.
Niektóre pola udostępniają dodatkowo tzw. tryb wyboru w etykiecie. W tym celu w etykiecie wprowadzany jest podwójny przycisk. Po kliknięciu tego przycisku otwierane jest menu kontekstowe, z którego można przejąć zdefiniowane wcześniej wzorce wyboru.
Przycisk dialogowy
W obszarze etykiety może zostać wstawiony dodatkowy przycisk. Przycisk ten jest z reguły wykorzystywany wtedy, gdy bezpośrednia lub pełna edycja danych w samym polu nie jest możliwa i do (dodatkowej) edycji ma zostać użyte okno dialogowe lub okno podpowiedzi wartości.
W artykule Instrukcja obsługi przycisk ten, ze względu na swój wygląd, jest określany jako przycisk rombu. W API jest on natomiast nazywany przyciskiem dialogowym, ponieważ odpowiada to jego typowej funkcji.
Klasa bazowa Field udostępnia jako wsparcie jedynie możliwość rejestrowania tzw. Dialog-Action. Po zarejestrowaniu takiej akcji przycisk rombu pojawia się automatycznie w obszarze etykiety pola. Po kliknięciu przycisku akcja zostaje wywołana.
Do obowiązków programisty należy zarejestrowanie dla tej akcji ActionListener oraz wykonanie odpowiedniego działania w metodzie actionPerform(). Ze względów spójności (zgodności z oczekiwaniami) powinno to zawsze oznaczać otwarcie okna dialogowego lub okna typu podpowiedź wartości, mimo że nie jest to technicznie wymuszone.
Przykład zastosowania przycisków dialogowych można znaleźć w rozdziale Korzystanie z dialogów.
Nieprawidłowe wartości
Konkretne podklasy klasy Field są przeznaczone dla określonych typów danych. Przykładowo pole CisDateField służy do wyświetlania obiektów typu com.cisag.pgm.datatype.CisDate oraz do umożliwienia ich wprowadzania lub edycji.
Jeżeli dane wprowadzone przez użytkownika nie mogą zostać przekształcone do tego typu danych (błędy składni, niedozwolone znaki itd.), metoda getValue() nie może zwrócić prawidłowego obiektu danego typu.
Dostępna jest metoda isValueValid(), która wskazuje, że wprowadzona wartość nie mogła zostać przekształcona do odpowiedniego obiektu. Metoda ta nie jest jednak pomocna w typowym modelu programowania, w szczególności dlatego, że klasy walidacji i logiki muszą być niezależne od klas UI i nie mogą bezpośrednio korzystać z ich metod.
W związku z tym wszystkie pola o nieprymitywnych typach danych zwracają w metodach getValue() (oraz getValueAsObject()) specjalne wartości. Wartości te mogą zostać bezpośrednio przeniesione do powiązanego obiektu biznesowego w metodzie dataFromUI() i są zazwyczaj traktowane przez klasy walidacji i logiki tak, jakby użytkownik pozostawił pole puste.
Dodatkowo pola rejestrują instancję obiektu zwróconą przez getValue() wraz z nieprawidłowym wpisem użytkownika. Jeżeli w późniejszym momencie zostanie wywołana metoda setValue() (lub setValueAsObject()), następuje sprawdzenie, czy przekazany obiekt został wcześniej zarejestrowany jako nieprawidłowy. Jeśli tak, w polu ponownie wyświetlany jest pierwotny (nieprawidłowy) wpis użytkownika.
Field API
Poniższe tabele przedstawiają najważniejsze oraz najczęściej używane metody klasy Field (pełna lista oraz szczegółowe opisy znajdują się w dokumentacji JavaDoc klasy Field):
Tabela 18: Wygląd wizualny
| Metoda | Zastosowanie |
| getHorizontalAlignment() setHorizontalAlignment(int) |
Odczyt, ustawianie lub zmiana wyrównania tekstu w obrębie pola (Field.LEFT, Field.CENTER oraz Field.RIGHT). |
| getLabelPosition() setLabelPosition(int) |
Odczyt, ustawianie lub zmiana względnej pozycji etykiety względem wartości. Obsługiwane są ustawienia: etykieta po lewej stronie wartości (Field.LABEL_LEFT) oraz etykieta nad wartością (Field.LABEL_TOP). |
Tabela 19: Dostęp do danych
| Metoda | Zastosowanie |
| getValueAsObject() setValueFromObject(Object) |
Odczyt lub zmiana aktualnej wartości pola. Metody te, oparte na java.lang.Object, umożliwiają ogólny dostęp do danych dla wszystkich typów pól. Podklasy klasy Field udostępniają dodatkowo metody getValue() oraz setValue(), których typ zwracany oraz typ parametru są zależne od typu danych danego pola. |
| getStringValue() setStringValue(String) |
Odczyt lub zmiana aktualnej wartości pola. Metody te, oparte na java.lang.String, umożliwiają ogólny dostęp do danych dla wszystkich typów pól. W przeciwieństwie do getValueAsObject() oraz setValueFromObject() zwracana jest jednak wyłącznie tekstowa reprezentacja wartości. Wskazówka Podklasy stosują częściowo różne procedury formatowania wartości jako tekstu lub tworzenia obiektu odpowiedniego typu danych na podstawie tekstu. |
| isValueValid() | Sprawdza, czy aktualna wartość jest prawidłowa. Więcej informacji można znaleźć w rozdziale Nieprawidłowe wartości. |
Tabela 20: Obsługa aplikacji
| Metoda | Zastosowanie |
| getDialogAction() setDialogAction(Action) |
Odczyt aktualnej Dialog-Action lub rejestracja nowej akcji.Zarejestrowana akcja zostaje wywołana po kliknięciu przez użytkownika przycisku dialogowego (rombu). |
| setDialogActionEnabled(boolean) | Ustawianie lub zmiana stanu enabled przycisku dialogowego. |
| setDialogButton(boolean) | Ustawianie lub zmiana widoczności przycisku dialogowego. |
| setSearchManager(SearchManager) | Zastosowanie własnej (alternatywnej) funkcji pomocy wyszukiwania wartości. Wiele podklas posiada już standardową funkcję pomocy uzupełniania wartości (np. kalendarz w polach daty lub wyszukiwanie na podstawie metadanych). Dzięki udostępnieniu własnej implementacji SearchManager możliwe jest włączenie specjalnych funkcji pomocy uzupełniania wartości. |
| setIndicator(Icon, String, int, int) | Wstawia dodatkową ikonę w polu (lub etykiecie).Taka ikona może służyć jako wskaźnik określonych stanów lub trybów. Wskazówka Funkcja ta jest częściowo wykorzystywana już przez standardowe zadania, np. w polach wielowartościowych (ikona: podwójna strzałka). |
| getObjectReference() setObjectReference(byte[]) |
Umożliwia powiązanie pola z referencją obiektu. Jeżeli do pola nie jest przypisana referencja obiektu, metoda getObjectReference() zwraca referencję obiektu zdefiniowaną w (bezpośrednim) kontenerze nadrzędnym. |
Tabela 21: Obsługa trybu wyboru
| Metoda | Zastosowanie |
| isSelectionMode() setSelectionMode(boolean) |
Sprawdza lub ustawia, czy pole działa w trybie wyboru.Większość pól w trybie wyboru zachowuje się jak pola tekstowe i umożliwia wprowadzanie wielu (wielowartościowych) wyrażeń lub zakresów. Tryb wyboru nie jest jednak obsługiwany przez wszystkie podklasy. |
| getSelectionString() | Zwraca wprowadzony przez użytkownika wyrażenie wyboru. |
Tabela 22: Obsługa metadanych
| Metoda | Zastosowanie |
| getLabel() setLabel(String) setLabelFrom(String) setLabelFrom(String, String[]) |
Odczyt, ustawianie lub zmiana tekstu etykiety. W przypadku pól tworzonych na podstawie metadanych (DataDescription) wartość ta jest automatycznie ustawiana. Jeżeli etykieta ma być wyświetlana w inny sposób (np. w tabelach), można ją pominąć za pomocą setLabel(null). |
| getColumns() setColumns(int) |
Odczyt lub zmiana szerokości pola w znakach. Na podstawie określonej liczby znaków pole automatycznie wyznacza (zależną od rozdzielczości) wartość preferredWidth. Wartość -1 powoduje, że preferredWidth (dynamicznie) dostosowuje się do aktualnej zawartości pola. W przypadku pól tworzonych na podstawie metadanych (DataDescription) wartość ta jest ustawiana automatycznie. Niektóre podklasy klasy Field obliczają jednak preferredWidth niezależnie od wartości columns. |
| getOriginalPath() | Zwraca ścieżkę w postaci przekazanej w konstruktorze. W przeciwieństwie do metody getPath() po wywołaniu add() zwracana jest pełna ścieżka. |
| setPath() | Niedozwolone — ścieżka metadanych musi zostać przekazana w konstruktorze. |
Tabela 23: Obsługa układu
| Metoda | Zastosowanie |
| getPreferredLabelWidth() setPreferredLabelWidth(int) |
Odczyt, ustawianie lub zmiana preferowanej (optymalnej) szerokości etykiety pola.Metoda ta jest wykorzystywana przez niektóre menedżery układu i nie powinna być wywoływana bezpośrednio. |
| getLabelWidth() setLabelWidth(int) |
Odczyt, ustawianie lub zmiana szerokości etykiety pola.Metoda ta jest wymagana przez niektóre menedżery układu i nie powinna być wywoływana bezpośrednio. |
Tabela 24: Implementacja
| Metoda | Zastosowanie |
| createUIField() | Tworzy element typu X-Element. Podklasy klasy com.cisag.pgm.gui.Field mogą nadpisać tę metodę, aby utworzyć oraz zainicjalizować własny X-Element. Metoda ta jest wywoływana przez obszar interfejsu użytkownika, gdy pole zostało dodane do kontenera (poprzez addToContainer()). |
| setUIElement(com..pgm.dialog.Field) | Rejestruje oraz inicjalizuje przekazany X-Element (wywołuje również super.setUIElement()). Podklasy muszą wywoływać tę metodę w swojej implementacji createUIField(). Jeżeli metoda ta zostanie nadpisana, należy również wywołać super.setUIElement(). Nadpisanie tej metody jest wymagane, jeśli podklasa implementuje własną referencję (zmienną instancyjną) do X-Element. |
| createTableEditor() | Tworzy nową instancję com.cisag.pgm.dialog.Field i konfiguruje ją do użycia jako edytor komórki w tabeli. Metoda ta jest wywoływana przez implementację tabeli. Podklasy klasy Field powinny ją z reguły nadpisywać, jeśli wykorzystywana jest specjalna implementacja X-Element. |
| copyFromUIField(com..pgm.dialog.Field) copyToUIField(com..pgm.dialog.Field) |
Dane z edytora komórki są kopiowane do pola poprzez copyFromUIField, a z pola do edytora komórki poprzez copyToUIField. Metody te są wywoływane przez implementację tabeli. Podklasy klasy Field muszą je zazwyczaj nadpisywać, jeśli wykorzystywana jest specjalna implementacja X-Element. |
| createInvalidValue() | Tworzy specjalną wartość nieprawidłową. Więcej informacji można znaleźć w rozdziale Nieprawidłowe wartości. |
| getInvalidKeyFor() | Zwraca klucz służący do rejestracji nieprawidłowych wartości w centralnym komponencie rejestrującym (InvalidValueSupport). Metoda ta jest wykorzystywana wewnętrznie i nie powinna być wywoływana bezpośrednio. |
| getInvalidStringFor(Object) | Zwraca pierwotne (błędne) dane wprowadzone przez użytkownika dla przekazanego obiektu.Metoda ta jest wykorzystywana wewnętrznie i nie powinna być wywoływana bezpośrednio. |
| putInvalidStringFor(Object, String) | Rejestruje określoną wartość wraz z odpowiadającym jej błędnym tekstem (wpisem użytkownika) w centralnym komponencie rejestrującym (InvalidValueSupport). Metoda ta jest wykorzystywana wewnętrznie i nie powinna być wywoływana bezpośrednio. |
| registerFieldMessages() registerFieldMessages(VisualElementve) |
Faktyczna rejestracja wszystkich wiadomości przy użyciu registerMessage lub wielu identyfikatorów (IDs). Metoda ta jest wywoływana przez obszar interfejsu użytkownika, gdy pole zostanie dodane do kontenera poprzez addToContainer(). Bezpośrednie wywołanie nie jest dopuszczalne. |
| createDataColumnSupport() | Udostępnia ogólną implementację eksportu danych, która zwraca zawartość pola jako tekst.Podklasy powinny nadpisywać tę metodę, jeśli wymagana jest bardziej specjalizowana implementacja. |
Kontenery najwyższego poziomu
Kontenery najwyższego poziomu stanowią korzeń hierarchii kontenerów. Do kontenerów najwyższego poziomu należą:
- okno główne (ApplicationWindow)
- dialogi modalne i niemodalne (Dialog)
- okna podpowiedzi wartości (PopupWindow)
Wspólną klasą bazową dla wszystkich kontenerów najwyższego poziomu jest com.cisag.pgm.gui.Window.
Kontenery najwyższego poziomu, czyli okna, stanowią korzeń hierarchii kontenerów, ale jednocześnie tworzą między sobą własną hierarchię. Ta hierarchia okien nie wynika z relacji zawiera, jak w przypadku kontenerów, lecz z relacji należy do (owned-by).
Okno uznaje się za należące do innego okna, jeżeli zostało przez nie utworzone. W tym sensie okno główne stanowi jedyny korzeń hierarchii okien.
Podczas tworzenia dialogów i okien typu podpowiedź wartości należy każdorazowo wskazać okno, które je utworzyło.
Hierarchia okien ma znaczenie w następujących aspektach:
- pozycjonowanie x/y na ekranie – względem okna nadrzędnego,
- pozycjonowanie z na ekranie – okno zależne powinno znajdować się przed oknem nadrzędnym,
- zamykanie okien – przy zamknięciu okna nadrzędnego wszystkie okna zależne są automatycznie zamykane,
- zmiana fokusu – po zamknięciu okna zależnego fokus automatycznie przechodzi do okna nadrzędnego,
- dialogi modalne – kilka dialogów modalnych może być widocznych jednocześnie wyłącznie wtedy, gdy są zagnieżdżone, tzn. gdy w hierarchii okien tworzą jedną linię.
Wszystkie kontenery najwyższego poziomu, czyli okna, po utworzeniu (instancjonowaniu) są początkowo niewidoczne, tzn. nie są wyświetlane na ekranie. Dopiero jawne wywołanie setVisible(true) powoduje otwarcie okna.
Za pomocą setVisible(false) okno może zostać ponownie zamknięte i tym samym usunięte z ekranu.
Możliwe jest wielokrotne otwieranie i zamykanie, czyli ponowne wykorzystanie okien, przy użyciu *setVisible(true/false)*. Ostateczne zniszczenie następuje dopiero przez mechanizm Garbage Collector.
ApplicationWindow
Klasa com.cisag.pgm.gui.ApplicationWindow reprezentuje okno główne Comarch ERP Enterprise. W przypadku klienta przeglądarkowego odpowiada ono oknu przeglądarki.
Dla każdej sesji istnieje dokładnie jedna instancja klasy com.cisag.pgm.gui.ApplicationWindow. Instancja ta jest tworzona przez silnik systemowy Comarch ERP Enterprise oraz obszar UI wraz z sesją.
Oprócz różnych obszarów informacyjnych i nawigacyjnych okno główne udostępnia również obszar aplikacji. W tym obszarze aplikacje UI są wyświetlane oraz uruchamiane.
Komunikacja między aplikacją UI a oknem głównym odbywa się wyłącznie za pośrednictwem metod klasy bazowej com.cisag.pgm.base.CisUiApplication lub automatycznie przez obszar UI bądź silnik systemowy. Bezpośredni dostęp aplikacji UI do okna głównego lub jednego z jego podkomponentów jest niedozwolony i może negatywnie wpływać na działanie wszystkich aplikacji.
Istnieje szczególny przypadek, w którym okno główne, czyli klasa ApplicationWindow, może być używane wewnątrz aplikacji UI.
Dialogi oraz okna typu popup podczas tworzenia zawsze wymagają wskazania okna nadrzędnego (owner window). Jeżeli w danym przypadku jest to jednoznacznie okno główne (więcej informacji znajduje się w rozdziale Dialog), wymaganą referencję można zazwyczaj uzyskać za pomocą metody getWindow() w klasie CisUiApplication.
Aby uniknąć sytuacji, w której wszystkie powiązane obiekty lub metody muszą posiadać referencję do CisUiApplication, możliwe jest również uzyskanie referencji do okna głównego poprzez statyczną metodę currentWindow() w klasie ApplicationWindow.
Dialog
Mimo że dialogi, w szczególności modalne, powinny być stosowane możliwie jak najrzadziej (więcej informacji na ten temat można znaleźć w artykule Styleguide), czasami istnieją przypadki, w których ich użycie jest nieuniknione.
W takich sytuacjach dostępna jest klasa com.cisag.pgm.gui.Dialog.
Za pomocą parametru w konstruktorze można określić, czy ma to być dialog modalny, czy niemodalny.
Podczas tworzenia dialogów należy używać konstruktora Dialog(int, VisualElement, String), ponieważ oferuje on następujące zalety:
Tabela 26: Stan ogólny
| Metoda | Zastosowanie |
| isVisible() setVisible(boolean) |
Służy do sprawdzania lub zmiany widoczności. Dialog staje się widoczny na ekranie dopiero po jawnym wywołaniu setVisible(true). Za pomocą setVisible(false) dialog może zostać zamknięty. |
| isModal() | Służy do sprawdzania modalności. Zwraca wartość true, jeżeli dialog został utworzony z parametrem Dialog.MODAL. Modalność, czyli zachowanie modalne lub niemodalne, jest określana wyłącznie w konstruktorze dialogu. |
| isResizable() setResizable(boolean) |
Określa, czy użytkownik może zmieniać rozmiar dialogu. Domyślnie ustawione jest true. Ustawienie setResizable(false) działa tylko wtedy, gdy wywołanie nastąpi przed setVisible(true). |
Tabela 27: Obsługa metadanych
| Metoda | Zastosowanie |
| getTitle() setTitle(String) setTitleFrom(String) setTitleFrom(String, String[]) |
Służy do odczytu, ustawiania lub zmiany tytułu dialogu. Określony tekst jest wyświetlany na pasku tytułu dialogu. Możliwe jest również ustawienie tytułu na podstawie tekstu konstrukcyjnego. Jeśli nie jest to możliwe, należy użyć jednej z metod setTitleFrom(). |
Tabela 28: Obsługa układu
| Metoda | Zastosowanie |
| getWidth() setWidth(int) getHeight() setHeight(int) |
Służy do odczytu, ustawiania lub zmiany szerokości oraz wysokości dialogu. Jawne ustawienie szerokości lub wysokości pozwala nadpisać automatyczne obliczenia (na podstawie wartości preferredWidth oraz preferredHeight panelu zawartości). Wartości podawane są w szerokościach znaków i odnoszą się do zewnętrznych wymiarów dialogu. |
Tabela 29: Implementacja
| Metoda | Zastosowanie |
| closed() | Pełni funkcję callback i może zostać nadpisana przez podklasy, które wymagają powiadomienia o zamknięciu dialogu. |
PopupWindow
Okna typu popup można traktować jako specjalną formę okien dialogowych. W porównaniu do standardowych dialogów wykazują one jednak następujące różnice:
- nie mogą być przesuwane przez użytkownika ani nie można zmieniać ich rozmiaru,
- są zawsze przypisane do innego elementu i otwierane w jego bezpośrednim sąsiedztwie,
- nie posiadają paska tytułu,
- nie występuje rozróżnienie na tryb modalny i niemodalny; zawsze jest to okno typu popup (automatyczne zamknięcie następuje po kliknięciu myszą poza obszarem okna).
Okna popup są wykorzystywane przede wszystkim przez obszar UI, np. jako podstawa dla pól kombi, menu rozwijanych (dropdown) lub podobnych elementów interfejsu użytkownika.
W aplikacjach użytkownika ich zastosowanie jest wskazane szczególnie wtedy, gdy istotne jest wizualne powiązanie z elementem otwierającym. Dobrym przykładem są przyciski wyboru rozwijanego.
Tabela 33: Pozycjonowanie
| Metoda | Zastosowanie |
| dockTo(VisualElement) | Pozycjonuje okno popup w pobliżu podanego elementu.Zwykle wywołanie tej metody nie jest konieczne, ponieważ element jest już przekazywany w konstruktorze. |
Kontenery standardowe
Obszar UI udostępnia szereg kontenerów standardowych. W odróżnieniu od konkretnych podklas com.cisag.pgm.gui.VisualElementContainer każda z tych podklas posiada specjalne właściwości dotyczące wyglądu, strukturyzacji oraz interaktywności.
View
Klasa View spełnia ten sam cel co java.awt.Panel w AWT lub javax.swing.JPanel w Swing. View jest kontenerem, który nie posiada żadnych specjalnych właściwości (kolor, ramka, tytuł itp.). W praktyce służy do grupowania innych elementów oraz ich rozmieszczania (za pomocą menedżerów układu).
W porównaniu do innych kontenerów (VisualElementContainer) klasa View nie udostępnia dodatkowej funkcjonalności, z wyjątkiem publicznych konstruktorów.
W interfejsach API Comarch ERP Enterprise często zamiast com.cisag.pgm.gui.VisualElementContainer stosowana jest klasa com.cisag.pgm.gui.View. Z tego względu com.cisag.pgm.gui.View stanowi również klasę bazową dla większości pozostałych kontenerów standardowych.
View API
Poniższe tabele przedstawiają najważniejsze oraz najczęściej używane metody klasy View (pełne oraz szczegółowe opisy znajdują się w dokumentacji JavaDoc klasy View).
Tabela 34: Konstruktory
| Konstruktor | Zastosowanie |
| View() View(LayoutManager) |
Tworzy prosty kontener.Opcjonalnie już w konstruktorze można przekazać menedżer układu. |
| View(String) View(String, LayoutManager) |
Ta grupa konstruktorów umożliwia dodatkowo określenie ścieżki. |
GroupBox
Podobnie jak np. View, klasa GroupBox posiada własne właściwości wizualne. GroupBox wyświetla tytuł, odstępy zewnętrzne (margins) oraz kolor tła, aby zawarte w niej elementy były grupowane nie tylko logicznie, lecz również wizualnie.
Zachowanie oraz wygląd klasy GroupBox można modyfikować za pomocą właściwości style:
Tabela 35: Właściwość style klasy GroupBox i Shelf
| Style | Zachowanie |
| STYLE_GROUPBOX | Standardowy GroupBox z tytułem oraz nieukrywalnym obszarem wyświetlania. |
| STYLE_SEPARATOR | Podobny do STYLE_GROUPBOX, jednak zamiast paska tytułu stosowana jest jedynie linia oddzielająca. |
| STYLE_SHELF | Obszar wyświetlania może być rozwijany lub zwijany przez użytkownika za pomocą dodatkowego przycisku w pasku tytułu (rozwiń/zwiń). Pasek tytułu może również zawierać inne elementy UI. |
| STYLE_SUBSHELF | Wariant Shelf z przezroczystym tłem (bez tytułu i bez paska tytułu). Brak wizualnego oddzielenia poprzez ramkę lub kolor tła. |
Zazwyczaj właściwość style jest jednak ustawiana automatycznie, np. poprzez wybór klasy lub obecność określonych właściwości (np. tytułu). Bezpośrednie ustawianie style najczęściej nie jest konieczne i powinno być unikane.
Klasa com.cisag.pgm.gui.GroupBox jest jednocześnie klasą bazową dla com.cisag.pgm.gui.Shelf (więcej informacji można znaleźć w rozdziale Shelf). Implementacja Shelf stanowi (w uproszczonym ujęciu) jedynie specjalną konfigurację klasy GroupBox.
W konstruktorze klasy GroupBox (oraz Shelf) domyślnie przypisywany jest menedżer układu StandardLayout. Menedżer ten nie jest jednak obowiązkowy i może zostać zastąpiony innym za pomocą metody setLayout().
GroupBox API
Poniższe tabele przedstawiają najważniejsze oraz najczęściej używane metody klasy GroupBox (pełna lista znajduje się w dokumentacji JavaDoc klasy GroupBox).
Tabela 36: Konstruktory
| Konstruktor | Zastosowanie |
| GroupBox(String) GroupBox(String, String) |
Tworzy klasę GroupBox (STYLE_GROUPBOX). Jako parametr można przekazać identyfikator GUID oraz opcjonalnie dodatkową ścieżkę. GUID służy do identyfikacji GroupBox (więcej informacji można znaleźć w rozdziale Ogólne właściwości). Na podstawie ścieżki (DataDescription lub StringTable) tytuł GroupBox jest ładowany z repozytorium. |
Tabela 37: Wygląd wizualny
| Metoda | Zastosowanie |
| STYLE_GROUPBOX STYLE_SEPARATOR STYLE_SHELF STYLE_SUBSHELF getStyle() setStyle(int) |
Służy do odczytu, ustawiania lub zmiany wyglądu oraz zachowania (style).Zazwyczaj styl jest określany automatycznie. |
| isCollapsible() | Zwraca wartość true, jeżeli użytkownik ma do dyspozycji przycisk do rozwijania/zwijania obszaru wyświetlania. Właściwość ta ma znaczenie wyłącznie dla klasy Shelf (style == STYLE_SHELF lub style == STYLE_SUBSHELF). Metoda ustawiająca tę właściwość (setter) jest dostępna jedynie w podklasie Shelf. |
| setMargins(boolean) | Określa, czy GroupBox ma używać standardowych odstępów zewnętrznych (margins), czy nie. |
Tabela 38: Stan ogólny
| Metoda | Zastosowanie |
| isCollapsed() setCollapsed(boolean) |
Służy do sprawdzania oraz zmiany widoczności obszaru wyświetlania. Jeżeli ustawiono collapsed == true, obszar wyświetlania jest ukrywany (zwinięty). Właściwość ta jest istotna wyłącznie dla klasy Shelf (style == STYLE_SHELF lub style == STYLE_SUBSHELF). |
Tabela 39: Obsługa metadanych
| Metoda | Zastosowanie |
| getTitle() setTitle(String) setTitleFrom(String) setTitleFrom(String, String[]) |
Służy do odczytu, ustawiania lub zmiany tytułu.\ Określony tekst jest wyświetlany w pasku tytułu GroupBox. Tytuł powinien być ustawiany już w konstruktorze poprzez przekazanie ścieżki lub za pomocą jednej z metod setTitleFrom(). |
Shelf
Klasa Shelf jest specjalną odmianą GroupBox (więcej informacji można znaleźć w rozdziale GroupBox) z możliwością zwijania obszaru wyświetlania.
Poprzez zwinięcie Shelf lub GroupBox obszar zawartości może zostać zredukowany do samego paska tytułu.
Klasa Shelf jest dostępna w dwóch wariantach:
- Typowy Shelf posiada, podobnie jak GroupBox, pasek tytułu oraz – tak jak GroupBox – specjalne kolory tła służące wizualnemu odgraniczeniu od innych elementów.
- Natomiast SubShelf nie wykorzystuje żadnego szczególnego obramowania ani koloru tła i nie posiada przycisku do rozwijania lub zwijania. Z tego względu jest szczególnie przydatny w przypadkach, gdy wewnątrz GroupBox lub Shelf potrzebna jest kolejna (zwijalna) grupa.
Wybór pomiędzy Shelf a SubShelf odbywa się automatycznie poprzez tytuł. Oznacza to, że jeżeli w obiekcie klasy Shelf zostanie ustawiony tytuł, jest on wyświetlany jako zwykły Shelf z paskiem tytułu. Jeżeli natomiast tytuł nie zostanie ustawiony, obiekt jest prezentowany jako SubShelf.
To zachowanie może być również sterowane jawnie poprzez właściwość style (więcej informacji można znaleźć w rozdziale GroupBox).
W przeciwieństwie do GroupBox, klasa Shelf obsługuje także rozszerzenie paska tytułu o dodatkowe elementy.
Za pomocą setHeader() można dodać tylko jeden element. Możliwe jest jednak przekazanie kontenera z wieloma elementami.
W przypadku dodawanego elementu należy jawnie ustawić właściwość alignmentX, np. na VisualElement.RIGHT_ALIGNMENT, jeśli element ma być wyświetlany po prawej stronie w pasku tytułu.
Shelf API
Poniższe tabele przedstawiają najważniejsze oraz najczęściej używane metody klasy Shelf (pełna lista znajduje się w dokumentacji JavaDoc klasy Shelf).
Tabela 40: Konstruktory
| Konstruktor | Zastosowanie |
| Shelf(String) Shelf(String, String) |
Tworzy klasę Shelf (STYLE_SHELF) lub SubShelf (STYLE_SUBSHELF). Decyzja, czy tworzony jest Shelf, czy SubShelf, podejmowana jest automatycznie na podstawie obecności tytułu (który może zostać ustawiony również później). Podobnie jak w klasie bazowej (GroupBox), jako parametr można przekazać GUID lub GUID wraz ze ścieżką. GUID służy do identyfikacji Shelf (więcej informacji można znaleźć w rozdziale Ogólne właściwości). Na podstawie ścieżki (DataDescription lub StringTable) tytuł Shelf jest ładowany z repozytorium. |
Tabela 41: Wygląd wizualny
| Metoda | Zastosowanie |
| setCollapsible(boolean) | Określa, czy przycisk rozwijania/zwijania ma być widoczny. Domyślnie ustawione jest true. |
| getHeader() setHeader(VisualElement) |
Służy do odczytu lub ustawiania dodatkowego elementu w obszarze paska tytułu. Można dodać tylko jeden element. Ponowne wywołanie setHeader() powoduje zastąpienie wcześniej ustawionego elementu. Możliwe jest przekazanie kontenera zawierającego elementy podrzędne. |
Tabela 42: Obsługa zdarzeń
| Metoda | Zastosowanie |
| setMaximizeAction(Action) | Rejestruje nową akcję Maximize-Action. Akcja ta może być wykorzystywana do synchronizacji widoczności kilku klas Shelf. Przykładem jest obszar nawigacyjny Comarch ERP Enterprise. Klasy Shelf Ulubione oraz Ulubione aplikacje są sterowane za pomocą tej akcji. Jeżeli jeden z wymienionych elementów zostanie otwarty, pozostałe zostaną jednocześnie zamknięte. W ten sposób aktywny Shelf otrzymuje maksymalną dostępną przestrzeń. Wyzwalaczem akcji jest zazwyczaj kliknięcie w tytuł Shelf. |
ScrollPane
Klasa com.cisag.pgm.gui.ScrollPane rozszerza klasę View (więcej informacji znajduje się w rozdziale View) o możliwość wyświetlania wyłącznie fragmentu zawartości kontenera.
Funkcjonalność ta ma znaczenie zawsze wtedy, gdy dostępna powierzchnia nie jest wystarczająca do pełnego wyświetlenia zawartości kontenera.
Klasa ScrollPane wyświetla z zawartości całego kontenera zawsze tyle, ile pozwala dostępna przestrzeń. Jednocześnie umożliwia użytkownikowi przesuwanie widocznego fragmentu. W ten sposób zapewniony jest dostęp do całej zawartości.
Przesuwanie zawartości odbywa się za pomocą pasków przewijania. Dla kierunku poziomego i pionowego przewidziany jest osobny pasek przewijania.
Podczas stosowania klas ScrollPane z punktu widzenia ergonomii należy uwzględnić następujące aspekty:
- Należy bezwzględnie unikać zagnieżdżania obszarów przewijanych (nie dotyczy to klas ScrollPane).
- Należy zrezygnować z poziomego przewijania, ponieważ z ergonomicznego punktu widzenia jest ono problematyczne podobnie jak zagnieżdżanie obszarów przewijanych.
- W przypadku list oraz tabel należy unikać pasków przewijania, jeżeli elementy te umożliwiają kartkowanie.
W klasie ScrollPane paski przewijania poziomy i pionowy mogą być konfigurowane niezależnie. Dostępne są trzy tryby (polityki):
- nigdy nie używać pasków przewijania (_SCROLLBAR_NEVER) – zawartość może zostać wówczas przycięta,
- zawsze używać/wyświetlać paski przewijania (_SCROLLBAR_ALWAYS) – paski są widoczne nawet wtedy, gdy nie są faktycznie potrzebne,
- używać pasków przewijania w razie potrzeby (_SCROLLBAR_AS_NEEDED) – paski są wyświetlane tylko wtedy, gdy są rzeczywiście wymagane.
Dla poziomych pasków przewijania w praktyce niemal zawsze stosowana jest wartość HORIZONTAL_SCROLLBAR_NEVER. Ryzyko przycięcia zawartości zwykle nie występuje, ponieważ dostępne elementy oraz menedżery układu są specjalnie zaprojektowane w taki sposób, aby zapewnić automatyczne dopasowanie zawartości do szerokości.
W przypadku przewijania pionowego obowiązuje następująca zasada: w całej hierarchii kontenerów danego elementu może znajdować się maksymalnie jeden kontener z ustawieniem VERTICAL_SCROLLBAR_AS_NEEDED. Dozwolone jest stosowanie wielu elementów/kontenerów z ustawieniem VERTICAL_SCROLLBAR_NEVER. Niektóre elementy (np. TabbedPane) częściowo wymuszają to zachowanie, sprawdzając przy dodawaniu do kontenera, czy jest on kontenerem przewijanym, i w razie potrzeby ustawiając wartość VERTICAL_SCROLLBAR_NEVER.
ScrollPane API
Poniższe tabele przedstawiają najważniejsze oraz najczęściej używane metody klasy ScrollPane (pełna lista oraz szczegółowe opisy znajdują się w dokumentacji JavaDoc klasy ScrollPane).
Tabela 43: Konstruktory
| Konstruktor | Zastosowanie |
| ScrollPane() ScrollPane(Layout) ScrollPane(String) ScrollPane(String, LayoutManager) |
Tworzy klasę ScrollPane z ustawieniami HORIZONTAL_SCROLLBAR_NEVER oraz VERTICAL_SCROLLBAR_AS_NEEDED. Opcjonalnie można przekazać menedżer układu i/lub ścieżkę |
| ScrollPane(int, int) ScrollPane(LayoutManager, int, int) ScrollPane(String, int, int) ScrollPane(String, LayoutManager, int, int) |
Tworzy klasę ScrollPane z określonymi zasadami (politykami) dla poziomego i pionowego paska przewijania. Opcjonalnie można przekazać menedżer układu lub ścieżkę. |
Tabela 44: Stan ogólny
| Metoda | Zastosowanie |
| HORIZONTAL_SCROLLBAR_AS_NEEDED HORIZONTAL_SCROLLBAR_NEVER HORIZONTAL_SCROLLBAR_ALWAYS getHorizontalScrollBarPolicy() setHorizontalScrollBarPolicy(int) |
Definiuje lub odczytuje zasady użycia poziomego paska przewijania. W praktyce ustawienie powinno być niemal zawsze ustawione na HORIZONTAL_SCROLLBAR_NEVER. |
| VERTICAL_SCROLLBAR_AS_NEEDED VERTICAL_SCROLLBAR_NEVER VERTICAL_SCROLLBAR_ALWAYS getVerticalScrollBarPolicy() setVerticalScrollBarPolicy(int) |
Definiuje lub odczytuje zasady użycia pionowego paska przewijania. Dokładnie jeden ScrollPane lub inny element przewijania powinien używać ustawienia VERTICAL_SCROLLBAR_AS_NEEDED. Wszystkie pozostałe powinny być ustawione na VERTICAL_SCROLLBAR_NEVER. |
SplitPane
Klasa com.cisag.pgm.gui.SplitPane jest kontenerem wywodzącym się z View (więcej informacji można znaleźć w rozdziale View), którego obszar wyświetlania podzielony jest na dwa regiony. Regiony te mogą być ułożone obok siebie lub jeden nad drugim.
Każdemu z tych regionów można przypisać dokładnie jeden VisualElement, przy czym jako VisualElement mogą być stosowane również kontenery.
Pomiędzy obydwoma regionami znajduje się linia podziału (Divider). Linia ta pełni jednocześnie funkcję uchwytu dla użytkownika. Poprzez jej przesuwanie jeden z regionów może być powiększany kosztem drugiego lub odwrotnie.
Dodawanie elementów do SplitPane musi odbywać się za pomocą specjalnych metod (np. setLeftComponent(VisualElement)). Użycie metod add(…) jest niedozwolone i prowadzi do niezdefiniowanego zachowania.
W przeciwieństwie do standardowych kontenerów rozmiar zawartych elementów nie jest określany przez menedżera układu (LayoutManager), lecz może być bezpośrednio zmieniany przez użytkownika. Ustawienie menedżera układu nie jest wymagane i nie jest obsługiwane.
SplitPane API
Poniższe tabele przedstawiają najważniejsze oraz najczęściej używane metody klasy SplitPane (pełna lista oraz szczegółowe opisy znajdują się w dokumentacji JavaDoc klasy SplitPane).
Tabela 45: Konstruktory
| Konstruktor | Zastosowanie |
| SplitPane(String) | Tworzy SplitPane z poziomym podziałem (HORIZONTAL_SPLIT). Parametr (GUID) służy do identyfikacji SplitPane (więcej informacji można znaleźć w rozdziale Właściwości ogólne). |
| SplitPane(String, int) SplitPane(String, int, double) |
Umożliwia określenie kierunku podziału (HORIZONTAL_SPLIT, VERTICAL_SPLIT) już podczas tworzenia. Dodatkowy parametr pozwala ustawić proporcję rozmiaru obu regionów (patrz setInitialDividerLocation()). |
Tabela 46: Wygląd wizualny
| Metoda | Zastosowanie |
| getDividerSize() setDividerSize(int) |
Określa szerokość linii podziału między obydwoma regionami. Szerokość ta jest predefiniowana przez obszar UI i nie powinna być zmieniana. |
Tabela 47: Stan ogólny
| Metoda | Zastosowanie |
| setInitialDividerLocation(int) setInitialDividerLocation(double) |
Programowo określa pozycję, w której ma znajdować się linia podziału (Divider). Pozycja linii podziału wpływa również na szerokość obu regionów. Wartość może zostać określona jako wartość bezwzględna (piksele) lub jako wartość względna (0.0 do 1.0). Wartość 0.5 oznacza położenie na środku. Odczyt aktualnej pozycji w tej wersji SplitPane nie jest możliwy. |
Tabela 48: Hierarchia kontenerów i zarządzanie układem
| Metoda | Zastosowanie |
| HORIZONTAL_SPLIT VERTICAL_SPLIT getOrientation() setOrientation(int) |
Określa, czy obszary wyświetlania mają być ułożone obok siebie (HORIZONTAL_SPLIT) czy jeden nad drugim (VERTICAL_SPLIT). |
| getLeftComponent() setLeftComponent(VisualElement) getRightComponent() setRightComponent(VisualElement) |
Dodaje VisualElement do lewego lub prawego regionu albo zwraca aktualny element w tym regionie. Metody te powinny być używane wyłącznie w przypadku poziomego podziału (orientation == HORIZONTAL_SPLIT). |
| getTopComponent() setTopComponent(VisualElement) getBottomComponent() setBottomComponent(VisualElement) |
Dodaje VisualElement do górnego lub dolnego regionu albo zwraca aktualny element w tym regionie. Metody te powinny być używane wyłącznie w przypadku pionowego podziału (orientation == VERTICAL_SPLIT). |
| clear() | Usuwa wszystkie elementy (regiony). Metoda ta powinna być stosowana zamiast remove() lub removeAll(). |
TabbedPane
Tabela 50: Wygląd wizualny
| Metoda | Zastosowanie |
| TOP BOTTOM LEFT RIGHT getTabPlacement() setTabPlacement(int) |
Określa pozycję zakładek (Tabs). Zakładki mogą być umieszczone opcjonalnie u góry (TOP), na dole (BOTTOM), po lewej (LEFT) lub po prawej (RIGHT) stronie TabbedPane. Ustawieniem domyślnym jest TOP. |
| DEFAULT_TABS SMALL_TABS EQUALS_TABS EXPANDED_TABS getTabStyle() setTabStyle(int) |
Określa rozmiar zakładek (Tabs).
Wartości te można ze sobą łączyć (operatorem OR). Jeśli nie zostanie ustawiona żadna wartość, obowiązuje DEFAULT_TABS. |
| NO_SCROLL_TAB_LAYOUT SCROLL_TAB_LAYOUT AUTO_SCROLL_TAB_LAYOUT getTabLayoutPolicy() setTabLayoutPolicy(int) |
Określa zachowanie obszaru zakładek w przypadku braku miejsca.
Ustawieniem domyślnym jest NO_SCROLL_TAB_LAYOUT. Zmiana tego ustawienia jest obsługiwana wyłącznie dla zakładek umieszczonych u góry (TOP). |
Tabela 51: Zarządzanie zakładkami
| Metoda | Zastosowanie |
| addTab(Action, VisualElement) | Tworzy nową zakładkę (Tab) dla przekazanego elementu i dodaje ją za ostatnią istniejącą zakładką.Z Action pobierane są tekst, ToolTip oraz/lub ikona, a także stan zakładki. |
| insertTab(Action, VisualElement, int) | Zachowanie jak przy addTab(), jednak nowa zakładka nie jest dodawana na końcu, lecz w określonej pozycji. |
| removeTab(Action) | Usuwa zakładkę powiązaną z przekazaną Action, czyli usuwa powiązany element. Zakładki muszą być usuwane tą metodą; użycie remove() lub removeAll() jest niedozwolone. |
| getTabCount() | Zwraca aktualną liczbę zakładek. |
| getActions() | Zwraca listę (java.util.ArrayList) wszystkich Action dla aktualnych zakładek. Lista może być modyfikowana. |
| getSelectedAction() setSelectedAction(Action) |
Przy getSelectedAction() zwracana jest Action odpowiadająca wybranej zakładce. setSelectedAction(Action) programowo wybiera zakładkę powiązaną z przekazaną Action. |
| getSelectedActionId() setSelectedActionId(int) |
Zachowanie analogiczne do getSelectedAction(), jednak zamiast Action używany jest jej identyfikator (ID). |
| getTabVisibility(Action) setTabVisibility(Action, boolean) |
Umożliwia ukrycie zakładki bez usuwania powiązanego elementu. |
| getTabVisibility(int) setTabVisibility(int, boolean) |
Jak przy getTabVisibility(Action, boolean), jednak zamiast Action używany jest jej identyfikator (ID). |
LayeredPane
Klasa com.cisag.pgm.gui.LayeredPane jest kontenerem dziedziczącym po View (więcej informacji można znaleźć w rozdziale View), który – podobnie jak TabbedPane – wyświetla zawsze tylko jeden ze swoich elementów podrzędnych.
W przeciwieństwie do TabbedPane, LayeredPane nie posiada elementów sterujących umożliwiających przełączanie wyświetlanego elementu podrzędnego. Przełączanie jest możliwe wyłącznie po stronie programu. Ponadto elementy podrzędne (określane tutaj jako Layer) muszą być instancjami klasy View (w przypadku TabbedPane zarządzane są instancje klasy VisualElement).
Dodawanie Layer, czyli elementów podrzędnych (View), w LayeredPane musi odbywać się przy użyciu specjalnych metod (addLayer(…), insertLayer(…)). Użycie metod add(…) jest niedozwolone i prowadzi do niezdefiniowanego działania.
LayeredPane API
Poniższe tabele przedstawiają najważniejsze i najczęściej używane metody klasy LayeredPane (pełna lista oraz szczegółowe opisy znajdują się w dokumentacji JavaDoc klasy LayeredPane).
Tabela 52: Konstruktory
| Konstruktor | Zastosowanie |
| LayeredPane() | Tworzy pusty obiekt LayeredPane. |
Tabela 53: Zarządzanie Layerami
| Metoda | Zastosowanie |
| addLayer(View) | Dodaje wskazany element (View) jako nowy Layer za ostatnim istniejącym Layerem. |
| insertLayer(View, int) | Działa analogicznie do addLayer, jednak umożliwia wstawienie nowego Layeru nie tylko na końcu, lecz również w dowolnej pozycji. |
| removeLayer(int) | Usuwa Layer znajdujący się na przekazanej pozycji. Elementy podrzędne (Layer) muszą być usuwane przy użyciu tej metody; użycie remove() lub removeAll() jest niedozwolone. |
| getLayerCount() | Zwraca aktualną liczbę Layerów. |
| getLayer(int) | Zwraca View (Layer) znajdujący się na przekazanej pozycji (indeksie). |
| getSelectedLayer() | Zwraca aktualnie wyświetlany (wybrany) View (Layer). |
| getSelectedIndex() | Zwraca indeks aktualnie wyświetlanego (wybranego) View. |
| setSelectedIndex(int) | Wyświetla View o wskazanym indeksie. |
Elementy standardowe
Label
Klasa com.cisag.pgm.gui.Label służy do wyświetlania statycznych (nieedytowalnych) tekstów oraz grafik (ikon). Tekst i ikona mogą być używane zarówno oddzielnie, jak i w połączeniu.
Label może pełnić funkcję etykiety dla innego elementu VisualElement. Przy użyciu metody setLabelFor() można utworzyć jawne powiązanie pomiędzy etykietą a powiązanym elementem. Powiązanie to jest wymagane, aby Label mógł przekazywać określone zdarzenia (kliknięcia myszy, klawisze dostępu itd.) do przypisanego elementu. Niektóre menedżery układu (np. StandardLayout) również wykorzystują to jawne powiązanie w celu odpowiedniego rozmieszczenia etykiety i elementu.
Label może być również używany jako hiperłącze. Oprócz odziedziczonej z VisualElement możliwości ustawienia LinkAction, przy użyciu metody setUrl() można zdefiniować adres URL jako cel hiperłącza.
Chociaż Label może zasadniczo służyć do wyświetlania każdego rodzaju nieedytowalnych danych, w Comarch ERP Enterprise dostępne są do tego celu wyspecjalizowane podklasy com.cisag.pgm.gui.Field.
Label API
Poniższe tabele przedstawiają najważniejsze i najczęściej używane metody klasy Label (pełna lista oraz szczegółowe opisy znajdują się w dokumentacji JavaDoc klasy Label).
Tabela 54: Konstruktory
| Konstruktor | Zastosowanie |
| Label() Label(Icon) Label(String) Label(String, Icon) Label(String, String) |
Tworzy obiekt Label. Wyświetlany tekst lub ikona mogą zostać określone opcjonalnie już w konstruktorze albo później przy użyciu metod setText(), setTextFrom() lub setIcon(). W konstruktorze specyfikacja tekstu zawsze odbywa się pośrednio, tj. poprzez ścieżkę (logiczny typ danych lub Stringtable). Sygnatury z dwoma parametrami umożliwiają dodatkowo przypisanie identyfikatora GUID do etykiety (sygnatury bez GUID są oznaczone jako deprecated). |
Tabela 55: Wygląd wizualny
| Metoda | Zastosowanie |
| LEFT RIGHT TOP BOTTOM CENTERgetHorizontalAlignment() setHorizontalAlignment(int) getVerticalAlignment() setVerticalAlignment(int) |
Poziome lub pionowe wyrównanie tekstu lub ikony wewnątrz etykiety można odczytać, ustawić lub zmienić (Label.LEFT, Label.CENTER, Label.RIGHT oraz Label.TOP, Label.CENTER, Label.BOTTOM). Ustawienia te mają znaczenie tylko wtedy, gdy etykieta jest większa niż wyświetlany tekst lub ikona.Właściwości horizontal-alignment i vertical-alignment nie powinny być mylone z odziedziczonymi z VisualElement właściwościami alignmentX i alignmentY. Te ostatnie służą jako wskazówki dla określonych menedżerów układu (layout managerów) i nie wpływają na wyrównanie wewnątrz etykiety, lecz na rozmieszczenie elementów względem siebie. Aby uzyskać oczekiwane wyrównanie, oba typy właściwości muszą być ze sobą odpowiednio dopasowane. |
| LEFT RIGHT TOP BOTTOM CENTERgetHorizontalTextPosition() setHorizontalTextPosition(int) getVerticalTextPosition() setVerticalTextPosition(int) |
Odczytanie lub ustawienie pozycji tekstu (względem ikony).Jeżeli etykieta zawiera zarówno ikonę, jak i tekst, właściwości horizontalTextPosition i verticalTextPosition określają, czy tekst ma być umieszczony po prawej, po lewej, nad, pod czy centralnie względem ikony. |
| getIconTextGap() setIconTextGap(int) |
Odczytanie lub ustawienie odstępu pomiędzy tekstem a ikoną.Jeżeli etykieta zawiera zarówno ikonę, jak i tekst, elementy te są oddzielone zdefiniowanym odstępem (w pikselach). |
| NONE HORIZONTAL VERTICALgetLineOrientation() setLineOrientation(int) |
Odczytanie, ustawienie lub zmiana prowadzenia linii. Obsługiwane są:
Właściwość ta ma znaczenie, gdy Label ma służyć jako etykieta dla innego elementu (patrz właściwość labelFor). |
Tabela 56: Wygląd wizualny
| Metoda | Zastosowanie |
| setIcon(Icon) | Określa ikonę wyświetlaną przez Label. |
| setDisabledIcon(Icon) | Umożliwia użycie specjalnej (dodatkowej) ikony dla stanu enabled=false. Jeżeli nie zostanie zdefiniowana specjalna ikona, używana jest standardowa reprezentacja dla tego stanu. |
Tabela 57: Wsparcie aplikacyjne
| Metoda | Zastosowanie |
| getUrl() setUrl(String) |
Odczytanie lub ustawienie hiperłącza (URL).Umożliwia powiązanie etykiety z adresem URL. Po kliknięciu przez użytkownika etykiety (tekstu i/lub ikony) wskazany adres URL zostaje otwarty w osobnym oknie. |
| setLabelFor(VisualElement) | Określa, że Label ma pełnić funkcję etykiety dla innego elementu VisualElement. Zachowanie i wygląd obu powiązanych elementów są wówczas zbliżone do pól (z wbudowaną etykietą). |
Tabela 58: Wsparcie metadanych
| Metoda | Zastosowanie |
| getText() setText(String) setTextFrom(String) setTextFrom(String, String[]) |
Odczytanie, ustawienie lub zmiana tekstu etykiety. W przypadku elementów tworzonych na podstawie metadanych (logiczny typ danych lub Stringtable) wartość ta jest ustawiana automatycznie. Jeżeli tekst nie ma być wyświetlany, można go usunąć przy użyciu setText(null). |
Button
Klasa com.cisag.pgm.gui.Button służy do wyświetlania przycisków (tzw. Buttons). Naciśnięcie takiego przycisku przez użytkownika generuje zdarzenie dla przypisanej aplikacji. Zazwyczaj w aplikacji zdarzenie to wywołuje akcję (Action) (więcej informacji można znaleźć w rozdziale Action, ActionListener).
Tworzenie i inicjalizacja przycisku odbywa się – podobnie jak w przypadku pól – z wykorzystaniem metadanych. Metadane przycisków nie pochodzą jednak, jak w przypadku pól, z logicznych typów danych ani z opisów bazodanowych, lecz z akcji.
Comarch ERP Enterprise Styleguide przewiduje różne typy i style przycisków. Oprócz standardowych przycisków (Push-Buttons) dostępne są przyciski z menu (Drop-Down Menus). Wybór konkretnego typu przycisku odbywa się poprzez konstruktor. W zależności od tego, czy akcja czy lista akcji zostanie przekazana do konstruktora, tworzony jest prosty przycisk (Push-Button) albo przycisk z menu (Drop-Down). Dodatkowo wygląd można dostosować do kontekstu za pomocą parametru type.
| Typ | Opis |
| Button.TEXT_BUTTON | Używany jest wyłącznie tekst (etykieta) z akcji.Przycisk korzysta ze standardowej ramki, przykładowo [OK] oraz [Anuluj] w typowych dialogach. |
| Button.ICON_BUTTON | Używana jest wyłącznie (mała) ikona z akcji.Przycisk posiada specjalną ramkę z efektem roll-over. Przykładowo, w Comarch ERP Enterprise praktycznie nie jest używany. |
| Button.HEADER_BUTTON | Używana jest wyłącznie (mała) ikona z akcji.Przycisk posiada specjalną ramkę z efektem roll-over. Przykładowo, wszystkie przyciski używane w normalnych paskach narzędzi (CoolBars). |
| Button.COOLBAR_BUTTON | Jak Button.HEADER_BUTTON, jednak używana jest tutaj większa ikona z akcji. Przykładowo, wszystkie przyciski używane w standardowym pasku symboli. |
| Button.MENU_BUTTON | Specjalny typ dla pozycji menu.Wykorzystywana jest (jeśli dostępna) zarówno ikona, jak i tekst z akcji. Przykładowo, menu kontekstowe. |
| Button.SMART_BUTTON | Specjalny typ dla pozycji menu Smart-Buttons.Wykorzystywana jest (jeśli dostępna) zarówno ikona, jak i tekst z akcji. Wskazówka Smart-Buttons nie stanowią odrębnego typu przycisku, lecz jedynie swoje (wewnętrznie generowane) pozycje menu. |
Klasa przycisku udostępnia kilka konstruktorów o różnych sygnaturach do tworzenia obiektów, np. Button(Action, int type, boolean showIcon, boolean showText).
Na podstawie przekazanego obiektu przycisk jest inicjalizowany z następującymi właściwościami:
- ikona – zależnie od parametru showIcon, rozmiar zależny od parametru type
- etykieta – zależnie od parametru showText
- ToolTip
- skrót klawiaturowy
- tekst pomocy
- status enabled/disabled
Metadane dotyczące akcji są zazwyczaj tworzone i pielęgnowane w repozytorium jako obiekt deweloperski Action.
Przycisk jest automatycznie aktualizowany w przypadku zmiany właściwości przypisanej akcji.
Jeżeli w konstruktorze nie zostanie przekazana pojedyncza akcja, lecz lista akcji albo akcja z Subactions, przycisk zostaje wyposażony w rozszerzone funkcje. Lewa część przycisku może bezpośrednio wywołać akcję domyślną, natomiast po prawej stronie wyświetlany jest przycisk strzałki (Drop-Down-Menu) ze wszystkimi akcjami zdefiniowanymi dla tej akcji. Tego typu przycisk określany jest jako SmartButton. Podobny rodzaj przycisku jest udostępniany przez klasę MenuButton (więcej informacji można znaleźć w rozdziale MenuButton).
Wyświetlanie SmartButton odbywa się w stanie normalnym (u góry), po najechaniu kursorem myszy (środek) oraz po rozwinięciu menu (na dole). Typowy jest symbol ikony oraz skierowana w dół strzałka po prawej stronie. Ikona symbolizuje aktualną akcję domyślną.
Menu rozwijane (Drop-Down) można otworzyć poprzez kliknięcie strzałki. Akcje wymienione na liście mogą zostać bezpośrednio wywołane poprzez kliknięcie albo naciśnięcie klawisza [ENTER].
Za pomocą parametrów showIcon oraz showText w konstruktorze klasy można określić, czy ikona, tekst czy oba elementy mają być widoczne.
Klasa Button udostępnia metodę setIcon(Icon ic). Umożliwia ona ustawienie zewnętrznych ikon, które nadpisują ikony dostarczane przez obiekt Action.
ToggleButton
ToggleButton różni się od Button tym, że może przyjmować jeden z dwóch stanów: zaznaczony (ToggleButton.SELECTED) albo niezaznaczony (ToggleButton.DESELECTED). Stany te różnią się wizualnie. Stan zaznaczony jest zazwyczaj przedstawiany jako wciśnięty przycisk.
Tworzenie instancji ToggleButton odbywa się analogicznie jak w przypadku elementów klasy nadrzędnej Button. Do konstruktora ToggleButton(String guid, Action action, int type, boolean showIcon, boolean showText) należy przekazać identyfikator GUID, obiekt Action, typ przycisku oraz parametry określające, czy mają być wyświetlane ikona i tekst.
W przypadku użycia konstruktora bez podania typu domyślnie przyjmowany jest Button.TEXT_BUTTON.
Stan początkowy ToggleButton jest określany przez obiekt Action za pomocą action.getState(). Każda zmiana stanu jest przekazywana do obiektu Action, który następnie informuje zarejestrowane ActionListener.
Aktualny stan można odczytać przy użyciu metody getState(). Zmiana stanu jest możliwa za pomocą setState(int state).
MenuButton
MenuButton jest w rzeczywistości menu rozwijanym (Drop-Down). Jego wygląd został jednak zaprojektowany w taki sposób, aby mógł być używany razem z innymi przyciskami w paskach narzędzi (więcej informacji można znaleźć w rozdziale CoolBar).
Przycisk MenuButton służy wyłącznie do otwierania menu rozwijanego (co jest sygnalizowane małą strzałką).
Podobnie jak wszystkie przyciski, również MenuButton pobiera swoje ikony, teksty oraz skróty klawiaturowe z odpowiednich obiektów Action. W przeciwieństwie do zwykłego przycisku wykorzystuje on jednak kilka akcji.
Jedna akcja jest przypisana bezpośrednio do MenuButton, natomiast pozostałe definiują jego pozycje menu. Akcje te są organizowane i udostępniane przez klasę pomocniczą com.cisag.pgm.gui.ActionList (więcej informacji można znaleźć w rozdziale ActionList). Klasa ta dziedziczy po com.cisag.pgm.gui.Action i w tej roli określa właściwości (ikona, tekst, skrót klawiaturowy oraz tekst pomocy) samego MenuButton. Zarejestrowane przy użyciu add Sub-Actions definiują odpowiednie właściwości poszczególnych pozycji menu.
MenuButton jest ściśle powiązany z tzw. SmartButton (więcej informacji można znaleźć w rozdziale Button). Główna różnica polega na tym, że MenuButton nie posiada DefaultAction i nie zapamiętuje ostatniego wyboru użytkownika. Z tego względu jego przycisk nie jest dzielony.
Przykład w rozdziale MenuButton pokazuje, jak utworzyć MenuButton i dodać go do paska narzędzi.
CheckBox
Klasa com.cisag.pgm.gui.CheckBox jest klasą pochodną com.cisag.pgm.gui.BooleanField (więcej informacji można znaleźć w rozdziale BooleanField) i rozszerza ją o możliwość zdefiniowania obiektu Action, który zostaje wywołany przy zmianie stanu.
Obiekt Action jest przekazywany do CheckBox w konstruktorze CheckBox(String guid, String path, Action action) wraz z identyfikatorem GUID oraz ścieżką.
RadioButton
Klasa com.cisag.pgm.gui.RadioButton jest klasą pochodną com.cisag.pgm.gui.ToggleButton i różni się od niej przede wszystkim graficzną reprezentacją elementu. RadioButton ma typowy wygląd znanego z systemu Windows przycisku opcji, składającego się z pola wyboru i etykiety.
Podobnie jak ToggleButton, RadioButton posiada dwa stany. Instancję typu RadioButton tworzy się za pomocą konstruktora RadioButton(String guid, Action action), który wymaga identyfikatora GUID oraz obiektu Action. Sterowanie stanami oraz propagacja zdarzeń przy ich zmianie są realizowane przez klasę nadrzędną.
ListBox
Do prezentacji prostej listy służy klasa com.cisag.pgm.gui.ListBox. Instancję tworzy się przy użyciu konstruktora ListBox(String guid).
Wypełnianie listy następuje po jej utworzeniu przy użyciu metod add(String text) lub add(String text, Object dataObject).
Przekazany parametr text jest wyświetlany na liście. Parametr dataObject jest powiązany z danym wpisem. Jeżeli dataObject nie zostanie przekazany, jako obiekt danych przyjmowany jest sam tekst. Elementy listy są dodawane w kolejności wywołań metody add().
Metoda getDataObjectAt(int index) zwraca wpis o podanym indeksie. Jeżeli taki wpis nie istnieje, metoda zwraca null. Metoda clear() usuwa całą zawartość listy.
Domyślnie lista umożliwia wybór wielu elementów (MULTIPLE_INTERVAL_SELECTION). Zmiana trybu wyboru odbywa się za pomocą metody setSelectionMode(int selectionMode), gdzie należy przekazać wartość:
- 0 dla SINGLE_SELECTION
- 1 dla SINGLE_INTERVAL_SELECTION
- 2 dla MULTIPLE_INTERVAL_SELECTION
Wybrane elementy można odczytać przy użyciu getSelectedDataObjects(). Metoda setSelectedDataObject(Object dataObject) umożliwia wymuszenie wyboru określonego elementu. Wywołanie clearSelection() usuwa bieżące zaznaczenie.
Obiekty ListBox obsługują mechanizm przewijania i w razie potrzeby wyświetlają pasek przewijania po prawej stronie. Metoda setVisibleRowCount(int visibleRowCount) pozwala określić preferowaną widoczną wysokość listy poprzez podanie liczby wierszy.
Pola
TextField
Elementy typu com.cisag.pgm.gui.TextField służą do wprowadzania i wyświetlania prostych (jednowierszowych) ciągów znaków.
Aby utworzyć obiekt TextField, do konstruktora należy przekazać identyfikator GUID oraz ścieżkę do metadanych.
Domyślnie pola tekstowe są enabled, czyli aktywne. Wywołanie metod setEditable(false) lub setEnabled(false) powoduje przełączenie pola odpowiednio w stan read only albo disabled. W takim przypadku wprowadzanie danych nie jest możliwe.
Pola nieedytowalne nadal mogą otrzymać fokus, a ich zawartość może zostać skopiowana do schowka (np. za pomocą skrótu klawiaturowego [Ctrl]+[C]).
Za pomocą odpowiedniego typu zawartości w metodzie setContentType(int type) zwykłe pole tekstowe może zostać przekształcone w pole do wysyłania wiadomości e-mail. W tym celu należy przekazać wartość TextField.CONTENT_TYPE_EMAIL. Kliknięcie pola powoduje otwarcie nowego okna klienta poczty elektronicznej z wstępnie uzupełnionym adresem podanym w polu.
Analogicznie można utworzyć pole URL, przekazując wartość TextField.CONTENT_TYPE_URL. Kliknięcie pola otwiera wskazany adres URL w nowym oknie przeglądarki.
Wizualizacja oraz zachowanie pól e-mail i URL podczas najechania kursorem odpowiadają elementom typu link.
Na podstawie ustawienia CONTENT_TYPE w DataDescription pole tekstowe może zostać przełączone w tryb CONTENT_TYPE_IDENTIFIER. Oznacza to, że zawartość pola może zawierać wyłącznie określone znaki dopuszczalne dla identyfikatora (ID), tj.:
- wielkie litery,
- cyfry,
- myślnik,
- podkreślenie,
- kropkę.
Przy każdej zmianie zawartości pole jest sprawdzane pod kątem niedozwolonych znaków i w razie potrzeby oznaczane czerwonym narożnikiem komunikatu.
Wprowadzanie niedozwolonych znaków w polach typu IDENTIFIER jest blokowane. Dodatkowo małe litery są automatycznie konwertowane na wielkie. Kombinacja klawiszy [CTRL]+[ALT]+[F12] umożliwia wyłączenie blokady wprowadzania znaków, a jej ponowne użycie – ponowne włączenie.
Pola działające w trybie wyboru (Selection Mode) nie przeprowadzają kontroli znaków IDENTIFIER. Nie jest również sprawdzana długość tekstu, nawet jeśli przewiduje to logiczny typ danych przypisany do pola. Konwersja małych liter na wielkie pozostaje jednak aktywna.
ExtendedTextField
ExtendedTextField to pole tekstowe rozszerzone o dodatkową, nieedytowalną część.
Ilustracja przedstawia ExtendedTextField bez fokusu (górny obraz) oraz z aktywnym fokusem. Opisowa część tekstowa jest wyświetlana po prawej stronie pola edytowalnego jasnoszarą czcionką.
Użytkownik może zmieniać wyłącznie część edytowalną tekstu. Część opisowa jest zawsze ustawiana przez system przy użyciu metody setDescription(String str).
ExtendedTextField może – jak każde pole – zostać wyposażone w pomoc wyboru wartości.
W konstruktorze ExtendedTextField, oprócz identyfikatora GUID i ścieżki, oczekiwany jest parametr logiczny description. Parametr ten określa, czy część opisowa ma być wyświetlana na stałe, czy jedynie jako podpowiedź (ToolTip). Zachowanie to można również kontrolować za pomocą metody setDescriptionAsTooltip(boolean enabled).
Podobnie jak TextField, również ExtendedTextField obsługuje typ zawartości CONTENT_TYPE_IDENTIFIER.
MultiLineTextField
Do wyświetlania tekstu wielowierszowego służy klasa com.cisag.pgm.gui.MultiLineTextField. Ilustracja przedstawia pole w stanie edytowalnym.
Tworzenie instancji pola odbywa się w standardowy sposób przy użyciu konstruktora
MultiLineTextField(String guid, String path).
Za pomocą metod setValue(String), setStringValue(String) lub setValueFromObject(Object) można ustawić lub zmienić tekst wyświetlany w polu. Odpowiadające im metody typu get() zwracają aktualną zawartość pola.
Programista aplikacji może określić wysokość MultiLineTextField w interfejsie użytkownika poprzez podanie liczby widocznych wierszy za pomocą metody setVisibleRowCount(int visibleRowCount).
Wartość domyślna wynosi 8 wierszy. Dopiero gdy zawartość nie mieści się w widocznym obszarze, po prawej stronie pola wyświetlany jest pionowy pasek przewijania.
Zawijanie wierszy odbywa się zawsze automatycznie. Poziomy pasek przewijania nie jest wyświetlany.
Pole wyświetla po prawej stronie obok etykiety symbol rombu, który po kliknięciu otwiera okno dialogowe. W oknie dialogowym zawartość jest edytowana, w razie potrzeby przekształcana i następnie przekazywana do pola. W tym przypadku samo pole nie jest edytowalne.
W aplikacjach MultiLineTextField jest często używane w połączeniu z funkcją National Language Support (NLS). Informacja, czy wsparcie NLS ma być aktywne, jest określana w DataDescription (LOCALIZATION_ENABLED == true).
EntityField
Abstrakcyjna klasa EntityField (com.cisag.pgm.gui.EntityField), będąca klasą pochodną com.cisag.pgm.gui.ExtendedTextField, stanowi klasę bazową dla wszystkich wariantów EntityField.
EntityField służy do reprezentowania obiektów biznesowych (Business Entities) na ekranie poprzez wyświetlanie ich identyfikatora (klucza merytorycznego) w części edytowalnej oraz tekstu opisowego w części nieedytowalnej.
Podczas inicjalizacji wszystkie EntityField są automatycznie powiązane z:
- Link-Action (hiperłącze do powiązanej aplikacji),
- Popup-Action (menu kontekstowe dla danego obiektu),
- pomocą wyboru wartości.
Ilustracja przedstawia typowe EntityField (com.cisag.app.general.gui.CountryField) z symbolem pomocy wyboru wartości. Symbol jest widoczny tylko wtedy, gdy wskaźnik myszy znajduje się nad elementem.
Tworzenie (implementacja) oraz użycie EntityField zostały opisane w Podręczniku programowania.
ValueSetField
ValueSetField służy do wprowadzania i wyświetlania danych opartych na typie danych ValueSet.
ValueSet (porównywalny z enumeracją) posiada z góry określony zakres wartości, zdefiniowany w repozytorium jako zbiór dyskretnych wartości typu short. Dla każdej wartości zdefiniowany jest dodatkowo tekst opisowy.
W trybie standardowym użytkownik może wybrać dokładnie jedną wartość z listy dostępnych opcji. Wygląd i zachowanie ValueSetField odpowiadają typowej (nieedytowalnej) liście rozwijanej (ComboBox).
Do wyświetlania w polu oraz na liście wyboru używane są teksty opisowe. Natomiast jako wartość w metodach getValue() i setValue() wykorzystywane są wartości typu short zdefiniowane w ValueSet.
Jeżeli ValueSetField nie jest skonfigurowane jako pole obowiązkowe (required == true), bieżący wybór można usunąć za pomocą klawisza [Enter], co powoduje wyczyszczenie pola.
W takim przypadku na liście wyboru dostępna jest również dodatkowa pozycja oznaczona jako (brak), umożliwiająca wyczyszczenie pola.
Obiekty ValueSetField są tworzone za pomocą konstruktorów: ValueSetField(String guid, String path) lub ValueSetField(String guid, String path, Action action).
Dodatkowo możliwe jest zarejestrowanie obiektu Action, którego Listener zostaną powiadomieni w przypadku zmiany wartości pola.
Dodatkowe wpisy można definiować lub dodawać przy użyciu metod:
- addValueSetItem(String externalName)
- addValueSetItem(short constantValue, String externalName)
Za pomocą metody setVisibleValues(short[] constantValues, boolean originalSorting) można ograniczyć wyświetlanie do określonych wartości i/lub zmienić ich kolejność sortowania.
W trybie standardowym metody getValueAsObject() oraz setValueFromObject(Object) działają analogicznie do getValue() oraz setValue(short) – zwracają lub ustawiają wybraną wartość.
Stała ValueSetField.UNDEFINED reprezentuje przypadek, w którym nie dokonano żadnego wyboru.
Tryb wyboru
W trybie wyboru ValueSetField umożliwia zaznaczenie więcej niż jednej wartości.
W interfejsie użytkownika zamiast listy rozwijanej (ComboBox) wyświetlane jest okno typu popup z polami wyboru (CheckBox). Ponieważ możliwe jest zaznaczenie więcej niż jednego elementu, wymagany jest przycisk [OK], który zatwierdza wybór.
Jako pomoc dostępne jest dodatkowe pole wyboru (wszystkie), umożliwiające zaznaczenie lub odznaczenie wszystkich pozycji jednym kliknięciem.
Po zamknięciu okna sposób wyświetlania w polu zależy od liczby wybranych pozycji:
- jeśli nie wybrano żadnej pozycji – wyświetlane jest (Brak),
- jeśli wybrano dokładnie jedną pozycję – wyświetlana jest ta pozycja,
- jeśli wybrano wszystkie pozycje – wyświetlane jest (wszystko),
- w pozostałych przypadkach – wyświetlane jest (wybór).
Jeżeli pole nie jest polem obowiązkowym, na końcu listy dodawane jest dodatkowe pole wyboru. Wybranie tej pozycji powoduje, że przy wyszukiwaniu uwzględniane są również rekordy posiadające wartość ValueSetField.UNDEFINED.
Pusta selekcja nie jest dozwolona (przycisk [OK] pozostaje nieaktywny). Wywołanie setValueFromObject(null) powoduje więc zaznaczenie wszystkich pozycji. Zachowanie to można zmienić przy użyciu metody setNoneAllowed().
W trybie wyboru metoda getValueAsObject() – podobnie jak getSelection() – zwraca tablicę (short[]) z wybranymi wartościami lub null, jeżeli nic nie zostało wybrane.
Analogicznie w trybie wyboru metoda setValueFromObject(Object) – podobnie jak setSelection(Object) – oczekuje tablicy (short[]) zawierającej wartości do zaznaczenia.
MultiValueSetField
MultiValueSetField stanowi rozszerzenie ValueSetField (wyłącznie w trybie wyboru). Umożliwia grupowanie pozycji wraz z możliwością grupowego zaznaczania i odznaczania.
Grupy przeznaczone do wyświetlania i wyboru są definiowane za pomocą odpowiednich ValueSet. Dodawanie grup odbywa się przy użyciu metody addValueSet(String path).
Parametr path określa logiczny typ danych (ValueSet) lub powiązaną z nim DataDescription.
Wartość pola, czyli aktualny wybór, jest reprezentowana przez tablicę dwuwymiarową (short[][]). Wartość tę można ustawić przy użyciu metod:
- setValue(short[][])
- setValueFromObject(Object)
Metoda setValueFromObject(Object) akceptuje dodatkowo listę (java.util.List) zawierającą obiekty typu short[]. Analogicznie:
- getValue() zwraca tablicę dwuwymiarową (short[][]),
- getValueAsObject() zwraca listę (java.util.List) zawierającą obiekty typu short[].
BooleanField
Definicja przycisków opcji, czyli pozycji w RadioButtonGroup, odbywa się – podobnie jak w przypadku ValueSetField – poprzez DataDescription.
Wywołanie metody setSelectedIndex(int index) w RadioButtonGroup powoduje ustawienie przycisku o podanym indeksie jako zaznaczony. Odpowiednio metoda getSelectedIndex() zwraca indeks aktualnie zaznaczonego przycisku.
Jeżeli w konstruktorze przekazany zostanie obiekt Action, jego Listener są powiadamiani przy każdej zmianie wyboru. W zarejestrowanym obiekcie Action za pomocą getState() można odczytać aktualny wybór lub powiązany z nim obiekt danych (najczęściej tekst opisowy).
Przyciski RadioButton mogą być również dodawane bezpośrednio do RadioButtonGroup przy użyciu metod:
- add(short value, String text)
- add(String text)
- add(String text, Object dataObject)
Za pomocą metody setVisibleValues(short[] constantValues, boolean originalSorting) można – analogicznie jak w ValueSetField – ograniczyć wyświetlanie do określonych wartości i/lub zmienić ich kolejność.
DecimalField
Klasa com.cisag.pgm.gui.DecimalField służy do wprowadzania i wyświetlania liczb dziesiętnych (com.cisag.pgm.datatype.CisDecimal).
Struktura klasy DecimalField jest podobna do klas typu IntegerField (więcej informacji można znaleźć w rozdziale ByteField, ShortField, IntegerField, LongField) i jest używana w analogiczny sposób.
Formatowanie wyświetlanych wartości odbywa się zgodnie z ustawieniami użytkownika (np. separator dziesiętny, grupowanie tysięcy itd.).
Liczba miejsc przed i po przecinku oraz wartości minimalne i maksymalne są przejmowane z DataDescription. Jeżeli w repozytorium nie określono specjalnych parametrów, obowiązują następujące wartości domyślne:
| Ustawienie domyślne | Opis |
| 15 | maks. liczba cyfr przed przecinkiem |
| 6 | maks. liczba cyfr po przecinku |
| 2 | min. liczba cyfr po przecinku |
Wprowadzanie danych przez użytkownika jest ograniczone do maksymalnej liczby cyfr; po przekroczeniu limitu klawiatura zostaje zablokowana. Jeśli za pomocą schowka zostanie wprowadzona nieprawidłowa wartość, pole zostaje oznaczone czerwoną ramką komunikatu.
W przypadku pól numerycznych deweloper ma możliwość zdefiniowania w repozytorium zakresu wartości (minimum, maksimum), którego wartość pola nie może przekroczyć ani nie może spaść poniżej.
CisDateField
Wartości daty oraz znaczniki czasu są w Comarch ERP Enterprise reprezentowane przez klasę com.cisag.pgm.datatype.CisDate. Klasa ta przechowuje nie tylko daną datę lub znacznik czasu, lecz również strefę czasową używaną w momencie wprowadzania danych. Dzięki temu wartość może zostać później wyświetlona dokładnie tak, jak została wprowadzona — nawet jeśli nastąpi to przez innego użytkownika w innej strefie czasowej.
Klasa CisDateField służy do wprowadzania i wyprowadzania wartości daty lub znaczników czasu opartych na CisDate. Ścieżka przekazywana w konstruktorze musi należeć do logicznego typu danych, który został wyprowadzony z jednego z następujących typów danych:
- com.cisag.pgm.datatype.CisObjectDate
- com.cisag.pgm.datatype.CisAttributeDate
- com.cisag.pgm.datatype.CisObjectDateUntil
- com.cisag.pgm.datatype.CisAttributeDateUntil
- com.cisag.pgm.datatype.CisObjectTimeStamp
- com.cisag.pgm.datatype.CisAttributeTimeStamp
Pierwsze cztery typy danych określają wartości daty, dwa ostatnie — znaczniki czasu. Różnice między CisObject* a CisAttribute* dotyczą wyłącznie reprezentacji w bazie danych i nie mają znaczenia dla pola.
Wartości daty oznaczone jako Until służą do szczególnego przypadku, w którym data ma być używana jako górna granica przedziału.
W DataDescription generowanej z logicznego typu danych wartości daty są reprezentowane przez typy Variant.LOCAL_DATE oraz Variant.LOCAL_DATE_UNTIL, natomiast wszystkie znaczniki czasu jako Variant.LOCAL_TIMESTAMP. Typ ten określa sposób obsługi obiektów CisDate przez pole.
W ustawieniu standardowym CisDateField wyświetla strefę czasową obiektu CisDate tylko wtedy, gdy różni się ona od bieżącego kontekstu. Nawet w takim przypadku nie jest ona edytowalna ani modyfikowalna. Za pomocą metody setTimeZoneEditable() pole może zostać skonfigurowane w taki sposób, aby użytkownik mógł zmieniać strefę czasową.
W odróżnieniu od wielu innych pól CisDateField wymaga jawnej inicjalizacji. Poprzez wywołanie setValue() do pola należy przekazać obiekt CisDate z prawidłową strefą czasową (GUID). W przypadku braku tej inicjalizacji pole nie może przy wywołaniu getValue() utworzyć obiektu CisDate i zgłasza wyjątek java.lang.IllegalStateException. Z tego samego powodu nie jest dozwolone ustawienie wartości pola bezpośrednio na null.
Za pomocą klasy com.cisag.pgm.datatype.CisDateUtility można tworzyć odpowiednie obiekty CisDate. Metoda createUndefinedCisDate(byte[] timeZoneGuid) zwraca przykładowo obiekt CisDate, za pomocą którego pole CisDateField może zostać zainicjalizowane lub ponownie wyczyszczone. Wybór właściwej strefy czasowej zależy od danego przypadku użycia.
Jeżeli strefa czasowa ma odpowiadać bieżącemu kontekstowi aplikacji, można ją ustalić w następujący sposób:
CisCalendar cisCalendar =
CisEnvironment.getInstance().getContext().
getCisCalendar();
byte[] tzguid = cisCalendar.getTimeZoneGuid()
Kolejną cechą szczególną pola jest obsługa walidacji i komunikatów. Sprawdzanie wprowadzonych danych musi być jawnie realizowane lub inicjowane przez daną aplikację. Aplikacja może (i powinna) korzystać z klasy pomocniczej com.cisag.pgm.util.RepositoryValidation, która udostępnia odpowiednie metody.
Aby błędy mogły zostać możliwie precyzyjnie przypisane do poszczególnych danych cząstkowych (tj. data, godzina, strefa czasowa) oraz odpowiednio zwizualizowane, pole wykorzystuje dodatkowe MessageIds dla swoich podpól. Za pomocą klas pomocniczych CisDateUtility oraz CisTimeStampUtility można odczytać te ogólne MessageIds.
CisDateField API
Poniższe tabele przedstawiają najważniejsze oraz najczęściej używane metody klasy CisDateField (pełna lista oraz szczegółowe opisy znajdują się w dokumentacji JavaDoc klasy CisDateField).
Tabela 59: Konstruktory
| Konstruktor | Zastosowanie |
| CisDateField(String, String) CisDateField(String, String, boolean) |
Tworzy obiekt CisDateField. Jako parametry należy przekazać GUID oraz ścieżkę (logiczny typ danych). Opcjonalnie można określić, że pole jest używane wyłącznie do wyświetlania danych (np. w listach). |
Tabela 60: Dostęp do danych
| Metoda | Zastosowanie |
| getValue() setValue(CisDate) |
Odczyt lub zmiana aktualnej wartości pola. Jak w przypadku wszystkich pól, dostępne są również metody dziedziczone z klasy nadrzędnej Field: getValueAsObject() oraz setValueFromObject(). |
Tabela 61: Wsparcie aplikacyjne
| Metoda | Zastosowanie |
| getSelectionMode() setSelectionMode(boolean) |
Nie jest obsługiwane przez CisDateField. W tym celu dostępne jest specjalne pole CisDateSelectionField. |
| isTimeZoneEditable() setTimeZoneEditable(boolean) |
Określa, czy strefa czasowa może być zmieniana przez użytkownika. |
| setTimeOutputFormatPattern(short) | Za pomocą tej metody można określić, czy sekundy lub milisekundy mają być zawsze wyświetlane, czy tylko wtedy, gdy są różne od 0. |
CisPointInTimeField
Pole to służy do wprowadzania i wyprowadzania terminów, przy czym jako dokładność terminu można wybrać milisekundę, dzień, (kalendarzowy) tydzień, miesiąc, kwartał lub rok.
Techniczna realizacja terminu odbywa się poprzez interfejs com.cisag.pgm.datatype.CisPointInTime, który definiuje termin za pomocą następujących właściwości:
- Calendar (CisCalendar)
- Accuracy (CisPointInTime.Accuracy)
- Value (String)
- Offset (String)
- Timestamp (java.util.Date)
- Precision (byte[])
Termin zawsze odnosi się do kalendarza, ponieważ dopiero on określa, kiedy rozpoczyna się dzień lub tydzień. Za pomocą właściwości Accuracy określana jest dokładność terminu, np. z dokładnością do dnia lub tygodnia. Właściwość Value umożliwia odczyt wartości w znormalizowanej postaci tekstowej.
Właściwość Offset pozwala na przesunięcie między reprezentacją zewnętrzną a wartością zapisaną wewnętrznie. Przykładowo termin tygodniowy z offsetem 5 dni powoduje, że jako wewnętrzny punkt odniesienia nie jest używany poniedziałek (0:00), lecz sobota (0:00) danego tygodnia. Offset — podobnie jak wartość (Value) — może być odczytywany w znormalizowanej postaci tekstowej.
Wartość wewnętrznego punktu odniesienia jest reprezentowana przez właściwość Timestamp. Właściwość Precision zwraca GUID obiektu biznesowego używanego do zapisu Accuracy i Offset.
Podobnie jak CisDateField (więcej informacji można znaleźć w rozdziale CisDateField), również CisPointInTimeField wymaga jawnej inicjalizacji. Inicjalizacja odbywa się poprzez wywołanie setValue(). W przypadku jej braku pole nie może przy wywołaniu getValue() utworzyć obiektu CisPointInTime i zgłasza wyjątek java.lang.IllegalStateException. Z tego samego powodu nie jest dozwolone ustawienie wartości pola bezpośrednio na null.
Za pomocą klasy com.cisag.pgm.datatype.CisPointInTimeUtility można tworzyć odpowiednie obiekty CisPointInTime. Metoda
createUndefinedCisPointInTimeMutable(CisCalendar calendar, short accuracy, String offset, byte[] precision)
zwraca przykładowo obiekt CisPointInTime, za pomocą którego pole CisPointInTimeField może zostać zainicjalizowane lub ponownie wyczyszczone. Wybór odpowiedniego kalendarza zależy od danego przypadku użycia.
Jeżeli kalendarz ma odpowiadać bieżącemu kontekstowi aplikacji, można go ustalić w następujący sposób:
CisCalendar cisCalendar =
CisEnvironment.getInstance().getContext().
getCisCalendar();
Również w zakresie walidacji i komunikatów CisPointInTimeField zachowuje się tak jak CisDateField. Sprawdzanie wprowadzonych danych musi być jawnie realizowane lub inicjowane przez daną aplikację. Aplikacja może (i powinna) korzystać z klasy pomocniczej com.cisag.pgm.util.RepositoryValidation, która udostępnia odpowiednie metody.
CisPointInTimeField API
Poniższe tabele przedstawiają najważniejsze oraz najczęściej używane metody klasy CisPointInTimeField (pełna lista oraz szczegółowe opisy znajdują się w dokumentacji JavaDoc klasy CisPointInTimeField).
Tabela 62: Konstruktory
| Konstruktor | Zastosowanie |
| CisPointInTimeField(String, String) CisPointInTimeField(String, String, boolean) |
Tworzy obiekt CisPointInTimeField. Jako parametry należy przekazać GUID oraz ścieżkę (logiczny typ danych). Opcjonalnie można określić, że pole jest używane wyłącznie do wyświetlania danych (np. w listach). |
Tabela 63: Dostęp do danych
| Metoda | Zastosowanie |
| getValue() setValue(CisPointInTime) |
Odczyt lub zmiana aktualnej wartości pola. Jak w przypadku wszystkich pól, dostępne są również metody dziedziczone z klasy nadrzędnej Field: getValueAsObject() oraz setValueFromObject(). |
TimeStampField
Pole TimeStampField służy do wprowadzania i wyprowadzania znaczników czasu opartych na java.util.Date (timestampów).
Ten typ znaczników czasu jest wykorzystywany głównie przez aplikacje systemowe, np. jako znacznik czasu w protokołach zmian lub komunikatów. Wartości daty i czasu wyświetlane przez TimeStampField zawsze odnoszą się do strefy czasowej z bieżącego kontekstu aplikacji.
Ścieżka przekazywana w konstruktorze musi należeć do logicznego typu danych wyprowadzonego z logicznego typu com.cisag.pgm.datatype.TimeStamp lub wykorzystywać znacznik czasu jako prymitywny typ danych. W DataDescription generowanej z logicznego typu danych wartości daty są reprezentowane przez typ Variant.TIMESTAMP.
Podobnie jak CisDateField (więcej informacji można znaleźć w rozdziale CisDateField), również TimeStampField wymaga jawnej inicjalizacji. Inicjalizacja odbywa się poprzez wywołanie setValue(). W przypadku jej braku pole nie może przy wywołaniu getValue() utworzyć obiektu Date i zgłasza wyjątek java.lang.IllegalStateException. Z tego samego powodu nie jest dozwolone ustawienie wartości pola bezpośrednio na null.
Za pomocą klasy com.cisag.pgm.datatype.CisTimeStampUtility można tworzyć odpowiednie obiekty Date. Stała UNDEFINED_TIME_STAMP definiuje przykładowo obiekt Date, za pomocą którego pole TimeStampField może zostać zainicjalizowane lub ponownie wyczyszczone.
Również w zakresie walidacji i komunikatów TimeStampField zachowuje się tak jak CisDateField. Sprawdzanie wprowadzonych danych musi być jawnie realizowane lub inicjowane przez daną aplikację. Aplikacja może (i powinna) korzystać z klasy pomocniczej com.cisag.pgm.util.RepositoryValidation, która udostępnia odpowiednie metody.
Aby błędy mogły zostać możliwie precyzyjnie przypisane do poszczególnych danych cząstkowych (tj. data, godzina) oraz odpowiednio zwizualizowane, pole wykorzystuje dodatkowe MessageIds dla swoich podpól. Za pomocą klasy pomocniczej CisTimeStampUtility można odczytać te ogólne MessageIds.
TimeStampField API
Poniższe tabele przedstawiają najważniejsze oraz najczęściej używane metody klasy TimeStampField (pełna lista oraz szczegółowe opisy znajdują się w dokumentacji JavaDoc klasy TimeStampField).
Tabela 64: Konstruktory
| Konstruktor | Zastosowanie |
| TimeStampField(String, String) TimeStampField(String, String, boolean) |
Tworzy obiekt TimeStampField. Jako parametry należy przekazać GUID oraz ścieżkę (logiczny typ danych). Opcjonalnie można określić, że pole jest używane wyłącznie do wyświetlania danych (np. w listach). |
Tabela 65: Dostęp do danych
| Metoda | Zastosowanie |
| getValue() setValue(java.util.Date) |
Odczyt lub zmiana aktualnej wartości pola. Jak w przypadku wszystkich pól, dostępne są również metody dziedziczone z klasy nadrzędnej Field: getValueAsObject() oraz setValueFromObject(). |
Tabela 66: Wsparcie aplikacyjne
| Metoda | Zastosowanie |
| getSelectionMode() setSelectionMode(boolean) |
Nie jest obsługiwane przez TimeStampField. W tym celu dostępne jest specjalne pole CisDateSelectionField. |
TimeOfDayField
Pole TimeOfDayField służy do wprowadzania i wyprowadzania godzin. Typem danych dla value jest java.util.Date, przy czym jako data zawsze musi być używana wartość 1.1.1970, tzn. godzina jest zapisywana jako milisekundy od północy.
Ścieżka przekazywana w konstruktorze musi należeć do logicznego typu danych wyprowadzonego z logicznego typu com.cisag.pgm.datatype.TimeOfDay lub wykorzystywać znacznik czasu jako prymitywny typ danych. W DataDescription generowanej z logicznego typu danych wartości daty są reprezentowane przez typ Variant.TIMESTAMP.
Podobnie jak CisDateField (więcej informacji można znaleźć w rozdziale CisDateField) oraz TimeStampField (więcej informacji można znaleźć w rozdziale TimeStampField), również TimeOfDayField wymaga jawnej inicjalizacji. Inicjalizacja odbywa się poprzez wywołanie setValue(). W przypadku jej braku pole nie może przy wywołaniu getValue() utworzyć obiektu Date i zgłasza wyjątek java.lang.IllegalStateException. Z tego samego powodu nie jest dozwolone ustawienie wartości pola bezpośrednio na null.
Za pomocą klasy com.cisag.pgm.datatype.CisTimeStampUtility można tworzyć odpowiednie obiekty Date. Stała UNDEFINED_TIME_STAMP definiuje przykładowo obiekt Date, za pomocą którego pole TimeOfDayField może zostać zainicjalizowane lub ponownie wyczyszczone.
Również w zakresie walidacji i komunikatów TimeOfDayField zachowuje się tak jak CisDateField. Sprawdzanie wprowadzonych danych musi być jawnie realizowane lub inicjowane przez daną aplikację. Aplikacja może (i powinna) korzystać z klasy pomocniczej com.cisag.pgm.util.RepositoryValidation, która udostępnia odpowiednie metody.
TimeOfDayField API
Poniższe tabele przedstawiają najważniejsze oraz najczęściej używane metody klasy TimeOfDayField (pełna lista oraz szczegółowe opisy znajdują się w dokumentacji JavaDoc klasy TimeOfDayField).
Tabela 67: Konstruktory
| Konstruktor | Zastosowanie |
| TimeOfDayField(String, String) TimeOfDayField(String, String, boolean) |
Tworzy obiekt TimeOfDayField. Jako parametry należy przekazać GUID oraz ścieżkę (logiczny typ danych). Opcjonalnie można określić, że pole jest używane wyłącznie do wyświetlania danych (np. w listach). |
Tabela 68: Dostęp do danych
| Metoda | Zastosowanie |
| getValue() setValue(java.util.Date) |
Odczyt lub zmiana aktualnej wartości pola. Jak w przypadku wszystkich pól, dostępne są również metody dziedziczone z klasy nadrzędnej Field: getValueAsObject() oraz setValueFromObject(). |
Tabela 69: Wsparcie aplikacyjne
| Metoda | Zastosowanie |
| getSelectionMode() setSelectionMode(boolean) |
Nie jest obsługiwane przez TimeOfDayField. |
| setTimeOutputFormatPattern(short) | Za pomocą tej metody można określić, czy sekundy lub milisekundy mają być zawsze wyświetlane, czy tylko wtedy, gdy są różne od 0. |
LabelField
Klasa com.cisag.pgm.gui.LabelField służy przede wszystkim do wyświetlania prostych tekstów (tylko do odczytu) w tabelach (więcej informacji można znaleźć w rozdziale Table). W tabelach (i listach) preferowane jest użycie LabelField zamiast odpowiednio skonfigurowanego TextField, ponieważ zużywa ono mniej zasobów.
Klasa com.cisag.pgm.gui.Label nie może być używana w tabelach, ponieważ kolumny tabel mogą być definiowane wyłącznie za pomocą instancji com.cisag.pgm.gui.Field. Z tego powodu LabelField jest klasą pochodną Field.
Instancje LabelField są tworzone za pomocą konstruktora LabelField(String guid, String path).
Metody setText(String value) oraz setValueFromObject(Object obj) służą do przypisania tekstu do pola LabelField. Aktualną zawartość pola można odczytać za pomocą getValueAsObject().
IconField
Klasa com.cisag.pgm.gui.IconField służy przede wszystkim do wyświetlania ikon w tabelach (więcej informacji można znaleźć w rozdziale Table).
Ikony są zazwyczaj wyświetlane za pomocą com.cisag.pgm.gui.Label (więcej informacji można znaleźć w rozdziale Label). Klasa Label nie może być jednak używana w tabelach, ponieważ kolumny tabel mogą być definiowane wyłącznie za pomocą instancji com.cisag.pgm.gui.Field. Z tego powodu IconField jest klasą pochodną Field. Klasa com.cisag.pgm.gui.LabelField pełni analogiczną funkcję dla tekstów.
Pole jest tworzone za pomocą standardowego konstruktora dla pól: IconField(String guid, String path).
Ikony, tj. instancje com.cisag.pgm.gui.Icon, mogą być przypisywane do pola za pomocą metod setValue(Icon) lub setValueFromObject(Object). Tekst podpowiedzi (ToolTip) dla pola można ustawić za pomocą metody setToolTipText(String text).
Nie jest możliwe przypisanie do IconField tekstu opisu (etykiety).
LayeredIconField
Klasa com.cisag.pgm.gui.LayeredIconField umożliwia jednoczesne wyświetlanie wielu ikon. Ikony są rozmieszczane jedna nad drugą (w kierunku osi Z). Jeśli widoczna ma być nie tylko najwyższa ikona, pozostałe ikony muszą być (przynajmniej częściowo) przezroczyste.
Ikony, które mają być wyświetlane w polu, należy dodać za pomocą metody addIcon(short value, Icon icon).
Wizualizacja ikon odbywa się poprzez wywołanie metody setValue(short[] value), przy czym tablica zawiera indeksy ikon, które mają być wyświetlone. Podobne działanie ma metoda setValueFromObject(Object obj), która jako parametr akceptuje wyłącznie tablicę typu short[].
Metody getValue() oraz getValueAsObject() zwracają indeksy aktualnie wyświetlanych ikon. Za pomocą metody containsIcon(short value) można sprawdzić, czy pole zawiera ikonę o podanym indeksie.
GuidField
Klasa com.cisag.pgm.gui.GuidField została stworzona specjalnie do wprowadzania i wyprowadzania identyfikatorów GUID.
Metody getValue() oraz setValue(byte[]) zwracają GUID w postaci tablicy (byte[]) lub akceptują GUID wyłącznie w tej formie. To samo dotyczy metod setValueFromObject(Object value) oraz getValueAsObject().
Za pomocą metody getStringValue() można uzyskać reprezentację GUID w postaci tekstowej (String).
FavoriteField
Pole com.cisag.pgm.gui.FavoriteField służy w tabelach do oznaczania wiersza jako wartości domyślnej lub ulubionej. Wszystkie komórki (CheckBox) w danej kolumnie tabeli są ze sobą zsynchronizowane.
Zawsze dokładnie jedna komórka (z n) może być zaznaczona. Zaznaczenie nowej komórki powoduje automatyczne odznaczenie poprzedniej. CheckBoxy w wierszach zachowują się zatem jak grupa przycisków RadioButton.
PasswordField
Do wprowadzania haseł dostępna jest klasa com.cisag.pgm.gui.PasswordField. PasswordField bazuje na TextField, jednak w interfejsie użytkownika zawsze wyświetla swoją zawartość — tekst wprowadzony przez użytkownika — jako ciąg znaków *.
Za pomocą metod getValue() oraz getValueAsObject() zawartość pola jest dostępna jako String. Wszystkie białe znaki na początku i/lub końcu tekstu są automatycznie usuwane (java.lang.String#trim()).
Metody setValue(String) oraz setValueFromObject(Object) umożliwiają zmianę hasła po stronie serwera.
CharacterField
Klasa com.cisag.pgm.gui.CharacterField służy do wprowadzania i wyprowadzania pojedynczego znaku.
Pole oczekuje jako wartości obiektu typu java.lang.Character, przekazywanego do metody setValueFromObject().
Jeżeli za pomocą odziedziczonej z klasy nadrzędnej com.cisag.pgm.gui.Field metody setStringValue(String) zostanie ustawiony łańcuch znaków o długości większej niż jeden znak, CharacterField przejmuje pierwszy znak z przekazanego ciągu.
Pola wyboru
W niektórych zastosowaniach, w szczególności przy wyszukiwaniu i w aplikacjach zapytań, konieczne jest wprowadzanie listy wartości, zakresu wartości (przedziału) i/lub wzorca wyszukiwania (z symbolami zastępczymi *, ?). W artykule Instrukcja obsługi pola te są określane jako pola wprowadzania wielowartościowego.
Od wersji 4 dostępne są w tym celu specjalne typy pól. Wszystkie nowe pola wyboru obsługują wzorce wprowadzania w formie listy wyboru. Ma to umożliwić użytkownikowi zapoznanie się ze standardowymi w Comarch ERP Enterprise sposobami zapisu selekcji.
Na liście wyboru wyświetlanych jest kilka propozycji selekcji, np. zaczyna się od ABC. Po wybraniu jednej z nich do pola przenoszony jest odpowiadający jej wzorzec, np. ABC*. Wzorzec ten może zostać dostosowany do indywidualnych potrzeb przed uruchomieniem wyszukiwania.
TextSelectionField
TextSelectionField służy jako pole wyboru dla wszystkich tekstowych typów danych.
TextSelectionField API
Poniższe tabele przedstawiają najważniejsze metody klasy TextSelectionField (pełna lista oraz szczegółowe opisy znajdują się w dokumentacji JavaDoc klasy TextSelectionField).
Tabela 70: Konstruktory
| Konstruktor | Zastosowanie |
| TextSelectionField(String, String) | Tworzy obiekt TextSelectionField. Jako parametry należy przekazać GUID oraz ścieżkę (logiczny typ danych). |
Tabela 71: Dostęp do danych
| Metoda | Zastosowanie |
| getValue() setValue(String) |
Odczyt lub zmiana aktualnej wartości pola. Jak w przypadku wszystkich pól, dostępne są również metody dziedziczone z klasy nadrzędnej Field: getValueAsObject() oraz setValueFromObject(). |
| getSelectionString() | Zwraca aktualną wartość w formacie wymaganym wewnętrznie przez wyszukiwanie. |
Tabela 72: Wsparcie aplikacyjne
| Metoda | Zastosowanie |
| getSelectionMode() setSelectionMode(boolean) |
Nie jest obsługiwane przez TextSelectionField (zawsze true). |
DecimalSelectionField
DecimalSelectionField służy jako pole wyboru dla następujących numerycznych typów danych:
- com.cisag.pgm.datatype.CisDecimal
- java.lang.Integer lub int
- java.lang.Long lub long
- java.lang.Short lub short
- java.lang.Byte lub byte
DecimalSelectionField API
Poniższe tabele przedstawiają najważniejsze metody klasy DecimalSelectionField (pełna lista oraz szczegółowe opisy znajdują się w dokumentacji JavaDoc klasy DecimalSelectionField).
Tabela 73: Konstruktory
| Konstruktor | Zastosowanie |
| DecimalSelectionField(String, String) | Tworzy obiekt DecimalSelectionField. Jako parametry należy przekazać GUID oraz ścieżkę (logiczny typ danych). |
Tabela 74: Dostęp do danych
| Metoda | Zastosowanie |
| getStringValue() setStringValue(String) |
Odczyt lub zmiana aktualnej wartości pola. Pole dostarcza lub oczekuje łańcuchów znaków w znormalizowanej postaci (zob. com.cisag.pgm.datatype.CisDecimalSelectionUtility). Jak w przypadku wszystkich pól, dostępne są również metody dziedziczone z klasy nadrzędnej Field: getValueAsObject() oraz setValueFromObject(). |
| getSelectionString() | Zwraca aktualną wartość w formacie wymaganym wewnętrznie przez wyszukiwanie. |
Tabela 75: Wsparcie aplikacyjne
| Metoda | Zastosowanie |
| getSelectionMode() setSelectionMode(boolean) |
Nie jest obsługiwane przez DecimalSelectionField (zawsze true). |
| isGroupingUsed() setGroupingUsed(boolean) |
Odczyt lub zmiana ustawienia grupowania cyfr. Ustawienie to określa, czy cyfry mają być grupowane za pomocą separatorów (np. kropki). Używany znak separatora zależy od indywidualnych ustawień użytkownika. |
CisDateSelectionField
CisDateSelectionField służy jako pole wyboru dla wszystkich typów danych opartych na dacie i czasie:
- com.cisag.pgm.datatype.CisDate
- java.util.Date
Podobnie jak CisDateField (więcej informacji można znaleźć w rozdziale CisDateField), również CisDateSelectionField wymaga jawnej inicjalizacji. Inicjalizacja odbywa się poprzez wywołanie setValue(). W przypadku jej braku pole nie może przy wywołaniu getValue() utworzyć obiektu CisDateSelection i zgłasza wyjątek java.lang.IllegalStateException. Z tego samego powodu nie jest dozwolone ustawienie wartości pola bezpośrednio na null.
Za pomocą klasy com.cisag.pgm.datatype.CisDateSelectionUtility można tworzyć odpowiednie obiekty CisDateSelection. Metoda createUndefinedCisDateSelection(CisCalendar calendar) zwraca przykładowo obiekt CisDateSelection, za pomocą którego pole CisDateSelectionField może zostać zainicjalizowane lub ponownie wyczyszczone. Wybór odpowiedniego kalendarza zależy od danego przypadku użycia.
Jeżeli kalendarz ma odpowiadać bieżącemu kontekstowi aplikacji, można go ustalić w następujący sposób:
CisCalendar cisCalendar =
CisEnvironment.getInstance().getContext().
getCisCalendar();
Również w zakresie walidacji i komunikatów CisDateSelectionField zachowuje się tak jak CisDateField. Sprawdzanie wprowadzonych danych musi być jawnie realizowane lub inicjowane przez daną aplikację. Aplikacja może (i powinna) korzystać z klasy pomocniczej com.cisag.pgm.util.RepositoryValidation, która udostępnia odpowiednie metody.
CisDateSelectionField API
Poniższe tabele przedstawiają najważniejsze metody klasy CisDateSelectionField (pełna lista oraz szczegółowe opisy znajdują się w dokumentacji JavaDoc klasy CisDateSelectionField).
Tabela 76: Konstruktory
| Konstruktor | Zastosowanie |
| CisDateSelectionField(String, String) | Tworzy obiekt CisDateSelectionField. Jako parametry należy przekazać GUID oraz ścieżkę (logiczny typ danych). |
Tabela 77: Dostęp do danych
| Metoda | Zastosowanie |
| getValue() setValue(CisDateSelection) |
Odczyt lub zmiana aktualnej wartości pola. Jak w przypadku wszystkich pól, dostępne są również metody dziedziczone z klasy nadrzędnej Field: getValueAsObject() oraz setValueFromObject(). |
| getTimeIntervals() | Zwraca aktualną wartość pola w postaci tablicy obiektów CisTimeInterval. |
| getSelectionString() | Zwraca aktualną wartość w formacie wymaganym wewnętrznie przez wyszukiwanie. |
Tabela 78: Wsparcie aplikacyjne
| Metoda | Zastosowanie |
| getSelectionMode() setSelectionMode(boolean) |
Nie jest obsługiwane przez CisDateSelectionField (zawsze true). |
| isTimeZoneEditable() setTimeZoneEditable(boolean) |
Określa, czy strefa czasowa może być zmieniana przez użytkownika. |
| setTimeOutputFormatPattern(short) | Za pomocą tej metody można określić, czy sekundy lub milisekundy mają być zawsze wyświetlane, czy tylko wtedy, gdy są różne od 0. |
AbstractCisQuantitySelectionField
Klasa AbstractCisQuantitySelectionField jest klasą bazową dla wszystkich wariantów pól wyboru typu Quantity z pakietu com.cisag.app.general.gui.
Do tej grupy należą przykładowo:
- DomesticAmountSelectionField
- DurationSelectionField
- ForeignAmountSelectionField
- QuantitySelectionField
ValidFromUntilFieldSupport
Aplikacje pracujące z obiektami biznesowymi zależnymi od czasu z reguły wymagają odpowiednich pól do wyświetlania oraz edycji przez użytkownika początku (From) i końca (Until) okresu obowiązywania.
Zasadniczo każda aplikacja może w tym celu utworzyć dwa oddzielne pola CisDateField oraz odpowiednio je skonfigurować i rozmieścić. Aby uprościć to zadanie oraz zapewnić jednolity wygląd i zachowanie, można skorzystać z klasy pomocniczej ValidFromUntilFieldSupport.
Klasa ta nie jest klasą pochodną Field ani VisualElement, lecz udostępnia jedynie metody służące do tworzenia odpowiednio skonfigurowanych elementów.
Klasa pomocnicza może być wykorzystywana na dwa sposoby:
- Za pomocą metody createSingleFieldRepresentation() można utworzyć specjalne pole, które wewnętrznie wykorzystuje dwa pola CisDateField jako podpola. Pole to można następnie dodać do kontenera.
- Alternatywnie — bez wywoływania metody createSingleFieldRepresentation() — podpola mogą być używane bezpośrednio i dodawane do kontenera oddzielnie.
ValidFromUntilFieldSupport API
Poniższe tabele przedstawiają najważniejsze metody klasy ValidFromUntilFieldSupport (pełna lista oraz szczegółowe opisy znajdują się w dokumentacji JavaDoc klasy ValidFromUntilFieldSupport).
Tabela 79: Konstruktory
| Konstruktor | Zastosowanie |
| ValidFromUntilFieldSupport(String, String, String, String) | Tworzy instancję ValidFromUntilFieldSupport. Dla pól From i Until należy przekazać po jednym GUID oraz ścieżce. Przekazane ścieżki muszą być kompatybilne z logicznymi typami danych wymaganymi przez CisDateField (więcej informacji można znaleźć w rozdziale CisDateField). Klasa udostępnia również konstruktor, w którym podawane są wyłącznie dwa identyfikatory GUID. W takim przypadku implementacja korzysta z ogólnych logicznych typów danych. Użycie tego konstruktora nie jest zalecane. |
Tabela 80: Dostęp do danych
| Metoda | Zastosowanie |
| getFromDate() | Odczyt aktualnej wartości From. Zwracany jest obiekt java.util.Date. Informacja o strefie czasowej jest zawsze identyczna jak dla wartości Until i może zostać tam odczytana. Alternatywnie można użyć metody getFromField(). Zwrócone pole CisDateField umożliwia np. wywołanie metody getValue(). |
| getUntilDate() | Odczyt aktualnej wartości Until. Zwracany jest obiekt com.cisag.pgm.datatype.CisDate. Zawarta w nim informacja o strefie czasowej obowiązuje również dla wartości From. Alternatywnie można użyć metody getUntilField(). Zwrócone pole CisDateField umożliwia np. wywołanie metody getValue(). |
| setValue(Date, CisDate) | Jednoczesna zmiana wartości From i Until. Wartość From należy przekazać jako java.util.Date, a wartość Until jako com.cisag.pgm.datatype.CisDate. Informacja o strefie czasowej zawarta w wartości Until obowiązuje dla obu wartości. |
Tabela 81: Wsparcie aplikacyjne
| Metoda | Zastosowanie |
| createSingleFieldRepresentation(String, String) | Tworzy zewnętrzne pole, które wewnętrznie zawiera dwa podpola dla From i Until. Parametry GUID i ścieżki odnoszą się do pola zewnętrznego (parametry dla pól wewnętrznych są przekazywane już w konstruktorze ValidFromUntilFieldSupport). Utworzone pole można dodać do kontenera jak każde inne pole. Do dostępu do danych należy jednak używać metod klasy ValidFromUntilFieldSupport. |
| getFromField() getUntilField() |
Dostęp do wewnętrznie używanych pól (CisDateField). Bezpośredni dostęp do tych pól jest wymagany np. w celu zarejestrowania właściwych Message-IDs dla danego podpola. |
| getSingleRepresentation() | Zwraca zewnętrzne pole. Warunkiem jest wcześniejsze jego utworzenie przy użyciu createSingleFieldRepresentation(). |
ValidFromFieldSupport
Aplikacje pracujące z obiektami biznesowymi zależnymi od czasu (wersjonowanymi) zazwyczaj wymagają odpowiednich elementów umożliwiających użytkownikowi wybór określonej wersji.
Zasadniczo każda aplikacja może samodzielnie utworzyć, odpowiednio skonfigurować i rozmieścić wymagane elementy. Aby uprościć to zadanie oraz zapewnić jednolity wygląd i zachowanie, zaleca się jednak użycie klasy pomocniczej ValidFromFieldSupport.
Podobnie jak klasa ValidFromUntilFieldSupport, klasa ta nie jest pochodną Field ani VisualElement, lecz udostępnia metody służące do tworzenia odpowiednio skonfigurowanych elementów.
Za pomocą metody createField() można utworzyć specjalne pole, które wewnętrznie składa się z następujących elementów:
- pola CisDateField, wyświetlającego datę początku obowiązywania dla danej wersji; data zakończenia obowiązywania jest wyświetlana jako podpowiedź (ToolTip),
- dwóch przycisków, za pomocą których użytkownik może przeglądać wersje (poprzednia / następna wersja).
Klasa pomocnicza udostępnia wyłącznie interfejs użytkownika; ładowanie lub tworzenie wersji musi zostać zaimplementowane przez aplikację.
Konstruktor ValidFromFieldSupport(String guid, String path, ActionListener) umożliwia zarejestrowanie obiektu ActionListener. Ten ActionListener jest powiadamiany, gdy użytkownik aktywuje jeden z przycisków. Na podstawie identyfikatora akcji przekazanego w metodzie performAction() listener może rozpoznać, który przycisk został wybrany przez użytkownika. Dla tych identyfikatorów zdefiniowano stałe ValidFromFieldSupport.NEXT_VERSION oraz ValidFromFieldSupport.PREV_VERSION.
Za pomocą metody setValue(com.cisag.pgm.datatype.CisObject) klasie pomocniczej można przypisać instancję czasowo zależnego obiektu biznesowego. Moment rozpoczęcia obowiązywania (CisObject#getValidFrom()) tego obiektu jest przenoszony do pola daty, natomiast moment zakończenia obowiązywania (CisObject#getValidUntil()) jest ustawiany jako podpowiedź (ToolTip).
Dwa przyciski utworzonego pola są ustawiane jako aktywne (enabled) lub nieaktywne (disabled) w zależności od wartości daty rozpoczęcia obowiązywania i daty zakończenia obowiązywania.
Listy i tabele
Do obsługi list i tabel dostępne są klasy:
- com.cisag.pgm.gui.List
- com.cisag.pgm.gui.Table
(więcej informacji można znaleźć w rozdziale Listy i tabele).
Między tymi klasami występują zasadnicze różnice dotyczące definiowania kolumn oraz budowy wierszy, istnieje jednak również szereg cech wspólnych, takich jak:
- podział na obszar nagłówka (Header), obszar danych (Body) oraz obszar stopki (Footer),
- budowa obszaru nagłówka,
- wykorzystanie modelu stronicowania (PageModel),
- reprezentacja rekordów danych przez instancje com.cisag.pgm.base.obj.CisListPartMutable,
- selekcja rekordów,
- eksport danych do pliku CSV.
Wspólne właściwości (więcej informacji można znaleźć w rozdziale Cechy wspólne) są zaimplementowane w klasie List. Klasa Table rozszerza List o specyficzne właściwości tabel (więcej informacji można znaleźć w rozdziale Table).
Istnieją również pewne właściwości zdefiniowane w klasie List, które nie są dostępne w tabelach (więcej informacji można znaleźć w rozdziale List).
Wspólne właściwości
Listy i tabele składają się maksymalnie z trzech obszarów:
- obszar nagłówka (Header),
- obszar danych (Body),
- obszar stopki (Footer).
Obszar nagłówka
W obszarze nagłówka list i tabel znajduje się u góry pasek narzędzi (com.cisag.pgm.gui.Coolbar), a poniżej nagłówki kolumn wyświetlanych danych.
Pasek narzędzi zawiera szereg elementów standardowych i może być rozszerzany o elementy własne.
Za pomocą metody setHeaderVisible(boolean) można sterować widocznością całego nagłówka.
Przy użyciu metod addHeaderElement() oraz addHeaderToggle() można dodawać do paska narzędzi dodatkowe elementy.
Metoda setHeaderExpandable(boolean value) umożliwia skonfigurowanie paska narzędzi w taki sposób, aby przy braku miejsca poszczególne elementy były przenoszone do okna popup. Szczegółowe wyjaśnienie tego mechanizmu znajduje się w rozdziale CoolBar.
Metoda addActionListener(ActionListener) pozwala na centralną rejestrację obiektu ActionListener dla wszystkich elementów należących do paska narzędzi.
PageButton
Do standardowego paska narzędzi list i tabel należy przycisk PageButton (więcej informacji można znaleźć w rozdziale PageButton).
Widocznością przycisku PageButton można sterować za pomocą metody setPageButtonVisible(boolean value).
Metoda setPageButtonListener(ActionListener) umożliwia reagowanie na zdarzenia (Actions) generowane przez PageButton. W takim przypadku zarejestrowany ActionListener musi przejąć obsługę przechodzenia do następnej lub poprzedniej strony, tj. sterowanie modelem danych i modelem stron.
Obszar danych
W obszarze danych (Body) rekordy modelu danych są wyświetlane strona po stronie (więcej informacji można znaleźć w rozdziale Model strony).
Model danych
Model danych list i tabel bazuje — jak opisano w rozdziale Listy i tabele — na liście rekordów, przy czym każdy rekord jest reprezentowany przez instancję com.cisag.pgm.base.obj.CisListPartMutable.
Obiekt ten pełni funkcję ogólnego kontenera danych. Właściwość data może służyć do przechowywania referencji do dowolnego obiektu. Pozostałe właściwości CisListPartMutable służą głównie do zapisywania informacji o stanie rekordu (np. zmieniony, oznaczony jako usunięty, zaznaczony itp.).
Zazwyczaj model danych zawiera jedynie część wszystkich istniejących rekordów, aby ograniczyć zużycie pamięci operacyjnej oraz czas odpowiedzi (czas ładowania). Przykładowo w przypadku wyników wyszukiwania do modelu wczytywane i wyświetlane są początkowo tylko pierwsze n znalezionych rekordów. Poprzez przewijanie stron użytkownik może zainicjować doładowanie kolejnych rekordów (więcej informacji można znaleźć w rozdziale PageButton).
Dodatkowo za pomocą metody setMaximumVirtualRows() można zdefiniować górną granicę liczby rekordów możliwych do załadowania.
Dodawanie rekordów odbywa się przy użyciu metod addRow(CisListPartMutable data) oraz insertRow(int aheadOf, CisListPartMutable data), zmiana (zastąpienie) rekordu — przy użyciu setRow(CisListPartMutable data), a usuwanie — przy użyciu removeRow(int rowIdx). Do usunięcia wszystkich rekordów służy metoda clear().
Za pomocą metody setAllRowsLoaded(boolean value) można poinformować listę/tabelę, czy model danych jest kompletny, tzn. czy zawiera wszystkie istniejące rekordy, czy też nie ma już kolejnych rekordów do załadowania. Informacja ta jest przekazywana do przycisku PageButton, który z kolei informuje użytkownika.
Selekcja
Listy i tabele umożliwiają użytkownikowi zaznaczanie rekordów. Za pomocą metody setSelectionMode() można określić, czy zaznaczony może być tylko jeden rekord, czy też wiele rekordów jednocześnie.
Aktualną selekcję można odczytać za pomocą metod getSelectedRow(), getSelectedRowList() oraz getSelectedRows(), a zmienić przy użyciu metod setRowSelected(int rowIdx) oraz clearSelections().
W ustawieniu standardowym selekcja może obejmować wyłącznie rekordy z bieżącej strony, tzn. przy każdej zmianie strony poprzednia selekcja jest tracona. Ustawienie to można zmienić za pomocą metody setPageSelectionEnabled().
Oprócz zaznaczania wierszy obsługiwana jest również tzw. OpenAction. Akcja zarejestrowana przy użyciu setOpenAction(Action a) jest wywoływana, gdy użytkownik otworzy wiersz za pomocą klawisza [ENTER] lub — w zależności od ustawień użytkownika — pojedynczego albo podwójnego kliknięcia.
Layout
Za pomocą metod setVisibleRowCount() oraz setPreferredHeightPolicy() można określić, ile miejsca lista/tabela powinna zajmować w nadrzędnym kontenerze.
Model stron
Zarówno lista (com.cisag.pgm.gui.List), jak i poszczególne ListView posiadają właściwość style. W przypadku listy właściwość ta jest ustawiana automatycznie:
- ListStyle.DIALOG dla list edytowalnych,
- ListStyle.OUTPUT dla list nieedytowalnych.
Za pomocą metody setStyle() można przypisać liście inny ListStyle.
W przypadku ListView właściwość style może zostać ustawiona w konstruktorze lub za pomocą metody setStyle(). Jeżeli nie zostanie ustawiona jawnie, wykorzystywana jest wartość zdefiniowana dla listy.
ListViewCreator
Obiekty ListView używane przez listę nie są tworzone bezpośrednio przez listę, lecz muszą zostać udostępnione przez aplikację. W tym celu aplikacja musi zaimplementować odpowiednią fabrykę i przekazać ją liście (np. w konstruktorze).
Interfejs com.cisag.pgm.gui.ListViewCreator określa API, które musi zostać zaimplementowane przez tę fabrykę:
public ListView createView(String viewName);
Lista wywołuje tę metodę zawsze wtedy, gdy do wizualizacji rekordu (reprezentowanego przez instancję com.cisag.pgm.base.obj.CisListPartMutable) potrzebuje odpowiedniej instancji com.cisag.pgm.gui.ListView.
Podczas wywołania metody lista przekazuje do fabryki właściwość viewName z odpowiadającego rekordu (CisListPartMutable). W razie potrzeby fabryka może na podstawie tego parametru zdecydować, w jaki sposób utworzyć lub skonfigurować instancję ListView. Wymaga to, aby rekordy dodawane przy użyciu addRow() lub insertRow() posiadały odpowiednio skonfigurowane viewName.
Lista żąda instancji ListView wyłącznie dla rekordów aktualnie wyświetlanej strony, a nie dla wszystkich załadowanych rekordów modelu. W miarę możliwości już utworzone instancje są ponownie wykorzystywane (recycled) dla kolejnych stron, a nie tworzone na nowo. Ponowne użycie jest jednak możliwe wyłącznie przy identycznej wartości viewName.
ListView
Abstrakcyjna klasa com.cisag.pgm.gui.ListView służy do wizualizacji rekordów z listy.
Lista deleguje wizualizację rekordów do tzw. ListView. Zadaniem tych ListView jest tworzenie odpowiednich elementów UI oraz wymiana danych pomiędzy rekordem a elementami UI.
ListView nie są powiązane z konkretną instancją rekordu, lecz z określonym typem rekordu. Jedna instancja implementacji ListView może (kolejno) wizualizować różne rekordy tego samego typu.
Abstrakcyjny interfejs com.cisag.pgm.gui.ListView definiuje API, które musi zostać zaimplementowane przez ListView:
abstract void dataToUi(CisListPartMutable row);
void dataFromUi(CisListPartMutable row);
Nadpisanie drugiej (pustej) metody nie jest konieczne, jeżeli lista jest wyłącznie listą wyjściową, tzn. nie jest wymagane mapowanie wsteczne danych wprowadzonych przez użytkownika do rekordów.
dataToUi()
Lista wywołuje metodę dataToUi zawsze wtedy, gdy powiązany rekord ma zostać wyświetlony lub zaktualizowany w wierszu bieżącej strony. W przypadku wielu rekordów wywołanie następuje oddzielnie dla każdego wiersza.
Zadaniem implementacji jest rozdzielenie danych z przekazanej instancji CisListPartMutable na elementy UI ListView. Zakres tych danych zależy od konkretnej aplikacji. Często właściwość data obiektu CisListPartMutable jest wykorzystywana do przechowywania referencji do instancji obiektu biznesowego. W takim przypadku wartości atrybutów obiektu biznesowego powinny zostać przeniesione do odpowiednich elementów UI.
Elementy UI ListView są zazwyczaj tworzone podczas jego instancjowania, tzn. w konstruktorze lub przez fabrykę (ListViewCreator), a następnie dodawane do ListView (kontenera).
dataFromUi()
Lista wywołuje metodę dataFromUi zawsze wtedy, gdy dane wprowadzone przez użytkownika w wierszu mają zostać zapisane w powiązanym rekordzie. W przypadku wielu rekordów wywołanie następuje oddzielnie dla każdego wiersza.
Zadaniem implementacji jest odczytanie aktualnych danych z elementów UI i zapisanie ich w przekazanej instancji CisListPartMutable. Jest to funkcja odwrotna do dataToUi.
ListStyle
Wygląd (kolory, obramowania itp.) jest określany na podstawie przypisanego ListStyle (więcej informacji można znaleźć w rozdziale ListStyle). Każdej instancji ListView można przypisać własny ListStyle. Zazwyczaj jednak przejmowany jest ListStyle z listy.
ExtendedListView
Klasa com.cisag.pgm.gui.ExtendedListView rozszerza ListView o nagłówek wiersza (row header), wymagany np. w listach dialogowych (więcej informacji można znaleźć w artykule Comarch ERP Enterprise Styleguide) lub w listach.
Udostępniane są jednak wyłącznie miejsce, układ oraz odpowiednie metody dostępu do nagłówka wiersza (Header) i obszaru danych. Konkretną zawartość muszą zapewnić klasy pochodne.
Jeżeli w nagłówku wiersza ma być wyświetlany status rekordu (CisListPartMutable), jako pomoc implementacyjną można wykorzystać klasę RowHeader i dodać ją do nagłówka za pomocą metody addHeaderPaneElement().
RowHeader
Klasa com.cisag.pgm.gui.RowHeader jest używana do wizualizacji statusu wyświetlanego rekordu w nagłówku wiersza (row header) list i tabel. W przypadku tabel odbywa się to automatycznie, natomiast w przypadku list należy odpowiednio zaimplementować ListView lub ExtendedListView.
Klasa RowHeader rozszerza klasę com.cisag.pgm.gui.Label o metodę
setRowStatus(com.cisag.pgm.base.obj.CisListPartMutable row). Wywołanie tej metody powoduje, że – w zależności od statusu przekazanego rekordu – etykieta zostaje opatrzona odpowiednim symbolem (ikoną). Analizowane są przy tym następujące właściwości:
- deleted
- selected
- persistent
- changed
Zazwyczaj klasa RowHeader jest używana w podklasach ExtendedListView (więcej informacji można znaleźć w rozdziale ExtendedListView). Obiekt RowHeader dodaje się do obszaru nagłówka ExtendedListView za pomocą metody addHeaderPaneElement(), a następnie w metodzie dataToUi() wywołuje się metodę setRowStatus(), aby wizualizować status rekordu za pomocą obiektu RowHeader.
Tabela
Klasa com.cisag.pgm.gui.Table jest pochodną klasy com.cisag.pgm.gui.List i dziedziczy po niej w szczególności stronicowy model danych.
Użycie tabel wymaga następujących kroków:
- utworzenie instancji Table
- zdefiniowanie kolumn tabeli
- zaimplementowanie TableDataManager (więcej informacji można znaleźć w rozdziale TableDataManager)
Tworzenie tabel
Tabelę tworzy się za pomocą konstruktora:
Table(String guid, int size, boolean editable, TableDataManager manager)
- Parametr guid przypisuje tabeli (VisualElement) identyfikator GUID.
- Parametr size określa (początkowy²) rozmiar strony tabeli.
- Parametr editable określa, czy tabela jest tabelą edytowalną, czy wyłącznie tabelą wyjściową³.
- Parametr manager przekazuje do tabeli tzw. TableDataManager. W razie potrzeby TableDataManager można również ustawić później za pomocą metody setTableDataManager(TableDataManager).
Definiowanie kolumn tabeli
W przeciwieństwie do listy, gdzie wizualizacja i mapowanie między rekordami a elementami UI są w pełni delegowane do ListView, w tabeli zastosowano inne podejście. Uwzględniono fakt, że dla wszystkich rekordów liczba i typ kolumn są identyczne. Dzięki temu definicja elementów UI nie musi być wykonywana dla każdego wiersza osobno, lecz tylko raz dla całej tabeli.
Dla każdej kolumny należy:
- utworzyć odpowiednie pole (Field),
- skonfigurować je,
- dodać do tabeli za pomocą metody addColumn(int idx, Field field).
Pola te pełnią następujące funkcje:
- na podstawie metadanych pola wyprowadzane są nagłówek kolumny, ToolTip, pomoc bezpośrednia itd.
- służą jako prototyp lub fabryka pól używanych w konkretnych komórkach tabeli (edytorów komórek)
- realizują mapowanie między rekordem a komórkami tabeli (więcej informacji można znaleźć w rozdziale TableDataManager)
W obszarze nagłówka wiersza (row header) można w podobny sposób dodać dodatkową kolumnę, np. do numerowania wierszy (numery pozycji), za pomocą *addRowNumber(Field field)*.
Widoczność nagłówków wierszy można włączać i wyłączać metodą setRowHeaderVisible(boolean).
Widoczność kolumn można kontrolować metodami:
- setColumnVisible(int idx, boolean visible)
- isColumnVisible(int idx)
Przewijanie poziome
Domyślnie tabela automatycznie dopasowuje szerokości kolumn do dostępnej łącznej szerokości. W przypadku tabel z dużą liczbą kolumn takie podejście nie zawsze jest pożądane.
Za pomocą metody setHorizontalScrollBarPolicy() można skonfigurować tabelę tak, aby przewijanie odbywało się kolumnami. W takim przypadku istnieje również możliwość zablokowania wybranych kolumn przy lewej krawędzi za pomocą metod fixColumn() lub fixColumns(), dzięki czemu pozostają one zawsze widoczne.
TableDataManager
Rekordy w tabeli są reprezentowane przez instancje com.cisag.pgm.base.obj.CisListPartMutable, natomiast kolumny tabeli przez instancje com.cisag.pgm.gui.Field.
Mapowanie rekordu na odpowiednie pola tabela deleguje do tzw. TableDataManager. Musi on zostać zaimplementowany w aplikacji i przekazany do tabeli (np. przez konstruktor).
Interfejs com.cisag.pgm.gui.TableDataManager definiuje API, które należy zaimplementować:
void dataToTable(CisListPartMutable row);
Implementacja drugiej metody może pozostać pusta, jeśli tabela jest wyłącznie tabelą wyjściową i nie jest wymagane odwrotne mapowanie danych wprowadzonych przez użytkownika na rekordy.
dataToTable()
Tabela wywołuje metodę dataToTable zawsze wtedy, gdy rekord ma zostać wyświetlony lub zaktualizowany w wierszu bieżącej strony. W przypadku wielu rekordów metoda jest wywoływana osobno dla każdego wiersza.
Zadaniem implementacji jest rozdzielenie danych z przekazanej instancji CisListPartMutable na zdefiniowane kolumny. Zakres tych danych zależy od konkretnej aplikacji. Często właściwość data obiektu CisListPartMutable jest używana do przechowywania referencji do instancji obiektu biznesowego. W takim przypadku wartości atrybutów obiektu biznesowego są przypisywane do zdefiniowanych kolumn.
Przez zdefiniowane kolumny rozumie się pola dodane do tabeli za pomocą addColumn(int idx, Field field). Przekazanie wartości odbywa się poprzez metody odpowiedniego pola (np. setValue()).
Oprócz samej wartości można również dostosować stan pola (enabled, editable itd.). Warunkiem jest dostęp implementacji TableDataManager do instancji Field zarejestrowanych przez addColumn().
Z tego względu w aplikacjach Semiramis interfejs TableDataManager jest zazwyczaj implementowany bezpośrednio w klasie aplikacji, a pola definiowane są jako zmienne instancyjne.
Po wywołaniu dataToTable tabela przejmuje wartości z pól do właściwych komórek tabeli. Proces ten powtarza się wiersz po wierszu, co oznacza, że wartości w polach są zawsze ważne tylko dla aktualnie przetwarzanego wiersza.
dataFromTable()
Tabela wywołuje metodę dataFromTable zawsze wtedy, gdy wartości komórek (wprowadzone przez użytkownika) mają zostać zapisane w odpowiadającym im rekordzie. W przypadku wielu rekordów metoda jest wywoływana osobno dla każdego wiersza.
Zadaniem implementacji jest odczytanie aktualnych wartości pól (zdefiniowanych kolumn) i zapisanie ich w przekazanej instancji CisListPartMutable.
Jest to zatem funkcja odwrotna do dataToTable.
Tabela automatycznie zapewnia, że w momencie wywołania metody aktualne wartości komórek danego wiersza znajdują się w instancjach Field.
PageButton
Tak zwany PageButton wyświetla użytkownikowi następujące informacje:
- numer aktualnej strony
- łączną liczbę (załadowanych) stron
- liczbę załadowanych rekordów (jako ToolTip)
- informację o kolejnych (jeszcze niezaładowanych) stronach/rekordach (+)
Za pomocą dwóch przycisków PageButton umożliwia przechodzenie między stronami (następna oraz poprzednia strona). Wprowadzenie numeru strony pozwala na bezpośrednie przejście do wybranej strony.
PageButton jest tworzony automatycznie przez listę (więcej informacji można znaleźć w rozdziale List) lub tabelę (więcej informacji można znaleźć w rozdziale Table) i wstawiany do nagłówka.
Metoda getPageButton() udostępnia obiekt PageButton aplikacji. Jest to wymagane np. w przypadku rejestracji ActionListener za pomocą metody setLoader(), aby w metodzie performAction() możliwe było doładowanie kolejnych rekordów.
Za pomocą metody setPage(int) można wyświetlić określoną stronę. Metoda getPage() zwraca numer aktualnej strony, a getCount() – łączną liczbę dostępnych wierszy.
Specjalne kontenery
CoolBar
Klasa com.cisag.pgm.gui.CoolBar służy do wyświetlania pasków narzędzi, stosowanych np. w oknie aplikacji lub w listach i tabelach.
Do konstruktora przekazywana jest stała typu int, określająca typ przycisków, które mają znajdować się na pasku narzędzi. Typ ten (więcej informacji można znaleźć w rozdziale Button) jest używany podczas tworzenia nowych przycisków przez CoolBar (na podstawie obiektu Action). Wartością domyślną (pusty konstruktor) jest Button.HEADER_BUTTON.
Konstruktor umożliwia również rejestrację ActionListener, który zostanie powiadomiony o wywołaniu akcji przez jedno z podelementów (np. przycisk).
Pasek CoolBar jest wypełniany przyciskami lub przyciskami przełączającymi (ToggleButton) za pomocą metod:
- add(Action action)
- add(Action action, int side)
- addToggle(Action action, int side)
Na podstawie przekazanego obiektu Action automatycznie tworzony jest odpowiedni przycisk.
Bez podania parametru side przyciski są wstawiane kolejno od lewej do prawej, przy czym ostatnio dodany element trafia na koniec po prawej stronie.
Jeżeli jako parametr zostanie przekazane CoolBar.RIGHT, rozmieszczanie odbywa się odwrotnie – od prawej do lewej. Parametr CoolBar.RIGHT_TO_LEFT powoduje umieszczenie elementu skrajnie po prawej stronie.
W celu wizualnego podziału można wstawić separator za pomocą metody addSeparator(int side).
Dostępne są metody remove(), umożliwiające usunięcie przycisku z paska narzędzi – również w tym przypadku przycisk jest identyfikowany przez odpowiadający mu obiekt Action.
Klasa CoolBar posiada właściwość expandable, którą można zmienić metodą setExpandable(boolean value) oraz odczytać metodą isExpandable(). Domyślnie ustawiona jest na false.
Właściwość expandable oznacza, że przyciski, które z powodu braku miejsca nie mogą zostać wyświetlone na pasku narzędzi, są przenoszone do okna typu popup. Aby otworzyć to okno, na skrajnym prawym końcu paska dodawany jest przycisk z odpowiednią ikoną.
CoolBar samodzielnie zarządza tworzeniem i usuwaniem okna popup oraz przycisku, a także automatycznie dostosowuje zawartość okna pop-up, np. przy zmianie rozmiaru okna aplikacji lub długości paska narzędzi.
ButtonBar
Klasa ButtonBar jest wykorzystywana głównie w dialogach, gdzie w dolnej części okna udostępnia przyciski służące do zatwierdzania danych, anulowania operacji itp.
Najprostszym sposobem dodania przycisków jest użycie ich identyfikatorów (int). Odpowiednie identyfikatory są łączone operacją OR (bitową), np.: ButtonBar.OK | ButtonBar.CANCEL i przekazywane do konstruktora.
Dostępne identyfikatory:
- ButtonBar.OK, ButtonBar.CANCEL
- ButtonBar.ACCEPT, ButtonBar.CLOSE
- ButtonBar.UPDATE, ButtonBar.RUN
- ButtonBar.VALIDATE, ButtonBar.DELETE
- ButtonBar.NONE
Instancje Action powiązane z tymi wewnętrznymi implementacjami przycisków można pobrać za pomocą odpowiednich metod get(), np. getOKButton() lub getCancelButton().
Klasa oferuje również metody dodawania przycisków w standardowy sposób:
- add(Action action)
- add(Action action, int side)
- add(Button b, int side)
Bez podania parametru side nowe elementy są zawsze wstawiane po lewej stronie. Aby umieścić przycisk po prawej stronie, należy przekazać wartość ButtonBar.RIGHT.
W konstruktorze można zarejestrować obiekt ActionListener, który będzie powiadamiany przy każdym wywołaniu przycisku.
Zdefiniowanie tzw. przycisku domyślnego za pomocą setDefault(int button) określa, który przycisk reaguje na naciśnięcie klawisza [ENTER]. Jako parametr przekazywany jest identyfikator Action. Wizualnie przycisk domyślny jest oznaczony ciemniejszą ramką. Domyślnie funkcję tę pełni przycisk [OK], jeżeli znajduje się w ButtonBar.
Metoda removeAll() usuwa wszystkie przyciski z paska.
W rozdziale Wykorzystanie dialogów znajduje się przykład zastosowania klasy ButtonBar z przyciskami [OK] i [Anuluj].
Menu
Klasa com.cisag.pgm.gui.Menu służy do tworzenia menu rozwijanych (Drop-Down), typowo wykorzystywanych w paskach menu (więcej informacji można znaleźć w rozdziale MenuBar).
Menu rozwijane składa się z przycisku (tytułu) oraz okna popup z listą pozycji menu (MenuItems), które otwiera się po aktywacji przycisku.
Klasa posiada pusty konstruktor oraz konstruktory o sygnaturze Menu(String path) oraz Menu(Action action). Tekst w tytule menu jest pobierany ze wskazanej ścieżki lub z obiektu Action za pomocą action.getText(). W przypadku przekazania obiektu Action, teksty ToolTip oraz pomocy bezpośredniej są również inicjalizowane na podstawie tego obiektu. Metoda getAction() zwraca obiekt Action zdefiniowany w konstruktorze.
Po utworzeniu instancji obiektu tytuł można ustawić za pomocą metod:
- setText(String text)
- setTitleFrom(String path)
- setTitleFrom(String path, String[] parameters)
Wypełnianie menu popup odbywa się za pomocą różnych metod add().
Metoda add(Action action) dodaje pozycję zachowującą się jak przycisk, reprezentowaną przez ikonę i tekst. Przekazany obiekt Action jest wywoływany po wybraniu pozycji (kliknięcie myszy lub naciśnięcie klawisza [ENTER]). Metoda addTextMenuItem(Action action) dodaje przycisk zawierający wyłącznie tekst. Metoda addCheckBoxMenuItem(Action action) umożliwia dodanie pozycji menu o charakterze pola wyboru (CheckBox). Metoda addSeparator() wstawia poziomą linię oddzielającą.
Za pomocą insert(int index, Action action) można dodać pozycję w wybranym miejscu. Pozycje menu popup można usuwać metodami remove(int pos) oraz removeAll().
Metoda getSubactions() zwraca listę wszystkich dodanych obiektów Action. Metoda getAction(int pos) zwraca obiekt Action znajdujący się na wskazanej pozycji.
MenuBar
Paski menu mogą być tworzone za pomocą klasy com.cisag.pgm.gui.MenuBar.
Typowe aplikacje Comarch ERP Enterprise nie posiadają jednak własnego paska menu, ponieważ centralny pasek menu jest udostępniany przez okno aplikacji.
Menu rozwijane dodaje się za pomocą metody addMenu(Menu menu), przy czym każde menu jest reprezentowane przez instancję com.cisag.pgm.gui.Menu. Metoda getMenus() zwraca tablicę aktualnie zdefiniowanych menu.
Za pomocą metod addActionListener(ActionListener l) oraz removeActionListener(ActionListener l) realizowana jest centralna rejestracja lub wyrejestrowanie ActionListener jednocześnie dla wszystkich menu.
MessageGroupView
Klasa com.cisag.pgm.gui.MessageGroupView rozszerza klasę com.cisag.pgm.gui.View o specjalne wsparcie dla grup komunikatów.
Klasa ta jest wykorzystywana głównie w sytuacjach, gdy walidacje nie są wykonywane na poziomie całej aplikacji, lecz np. na poziomie poszczególnych widoków. Typowym przykładem są wiersze list (więcej informacji można znaleźć w rozdziale ListView).
Specjalne elementy
Tree
Klasa com.cisag.pgm.gui.Tree służy do wizualizacji struktur drzewiastych.
Na poniższej ilustracji przedstawiono użycie klasy Tree na przykładzie katalogu aplikacji Comarch ERP Enterprise. Pokazana struktura jest jednopoziomowa, ponieważ pod węzłem katalogu znajdują się wyłącznie elementy liściowe, bez kolejnych węzłów podrzędnych.
Zasadniczo drzewo umożliwia tworzenie struktur o dowolnej głębokości.
Konstruktor Tree(String guid, TreeNode root, Action action, TreeViewCreator creator, EditableTreeViewCreator editableCreator) określa, jakie obiekty są wymagane do utworzenia i inicjalizacji struktury drzewa.
Podobnie jak w przypadku większości elementów, należy podać identyfikator GUID. Jako element root należy przekazać instancję com.cisag.pgm.gui.TreeNode (więcej informacji można znaleźć w rozdziale TreeNode), która może już zawierać całą strukturę drzewa. Na podstawie tego obiektu tworzony jest wewnętrzny model danych drzewa. Jeżeli nie zostanie przekazany węzeł root, konstruktor utworzy pusty model drzewa.
Za pomocą parametru action można zarejestrować instancję com.cisag.pgm.gui.Action, której ActionListener ma zostać powiadomiony, gdy użytkownik aktywuje (otworzy) element. Zdarzenie to, określane również jako OpenAction, może zostać wywołane klawiszem [ENTER] lub – w zależności od ustawień użytkownika – pojedynczym bądź podwójnym kliknięciem myszy.
Za pomocą parametru creator można przekazać fabrykę (com.cisag.pgm.gui.TreeViewCreator) służącą do tworzenia specjalnie dostosowanych instancji com.cisag.pgm.gui.TreeView (więcej informacji można znaleźć w rozdziale TreeView). Jeżeli taka fabryka nie zostanie podana, węzły drzewa są wizualizowane za pomocą generowanych standardowo instancji TreeView.
Jeżeli węzły drzewa mają być bezpośrednio edytowalne, należy za pomocą parametru editableCreator przekazać odpowiednią fabrykę (com.cisag.pgm.gui.EditableTreeViewCreator). Jej zadaniem jest tworzenie odpowiednio skonfigurowanych instancji com.cisag.pgm.gui.EditableTreeView (więcej informacji można znaleźć w rozdziale EditableTreeView).
Metody addNode(TreeNode node) oraz addNode(TreeNode parent, TreeNode node) rozszerzają strukturę drzewa o kolejne elementy, przypisując węzły do ich węzła nadrzędnego. Węzeł nadrzędny pochodzi z node.getParent() lub jest przekazywany jawnie. Jeżeli nie można zidentyfikować węzła nadrzędnego, obiekt TreeNode zostaje dodany do węzła root.
Funkcja insertNode(TreeNode aheadOf, TreeNode node) wstawia nowy element (node) bezpośrednio przed wskazanym węzłem lub elementem liściowym aheadOf. Oba elementy mają wówczas tego samego rodzica.
Nowy węzeł root można ustawić za pomocą setRoot(TreeNode node) lub setRoot(String data). W drugim przypadku obiekt TreeNode tworzony jest automatycznie na podstawie parametru data. Metoda getRoot() zwraca aktualny węzeł główny.
Metoda removeNode(TreeNode node) usuwa wskazany węzeł, removeChildren(TreeNode parent) usuwa wszystkie węzły podrzędne danego elementu nadrzędnego, natomiast removeAll() usuwa całą strukturę drzewa.
Metoda findNode(byte[] guid) umożliwia wyszukanie węzła na podstawie jego GUID.
Rozwijanie i zwijanie węzłów drzewa (folderów) odbywa się z poziomu użytkownika poprzez kliknięcie lewym przyciskiem myszy – w zależności od ustawień może to być pojedyncze lub podwójne kliknięcie. Z poziomu klawiatury służą do tego klawisze strzałek w prawo (rozwinięcie) i w lewo (zwinięcie).
Po stronie serwera dostępne są metody:
- expandNode(TreeNode node)
- collapseNode(TreeNode node)
- expandAll()
- collapseAll()
Stan węzła można sprawdzić metodami isExpanded(TreeNode node) oraz isCollapsed(TreeNode node).
Metody setExpandAction(Action a) oraz setCollapseAction(Action a) umożliwiają rejestrację akcji, których ActionListener będą powiadamiane przy rozwijaniu lub zwijaniu węzła.
Funkcja editNode(TreeNode node) przełącza wskazany węzeł w tryb edycji, o ile – jak opisano wyżej – zarejestrowano EditableTreeViewCreator jako fabrykę dla obiektów EditableTreeView. W ten sposób można zmienić oznaczenie węzła.
Widok drzewa umożliwia wybór pojedynczy (ustawienie domyślne) lub wielokrotny, który można ustawić metodą setSelectionMode(int mode), przekazując stałe:
- Tree.SINGLE_SELECTION
- Tree.MULTIPLE_SELECTION
Aktualnie zaznaczone węzły można pobrać metodami getSelectedNode() oraz getSelectedNodes(). Metoda isSelected(TreeNode node) pozwala sprawdzić, czy dany węzeł należy do zaznaczenia. Metoda clearSelection() usuwa całe zaznaczenie.
Możliwe jest również zarejestrowanie akcji za pomocą setSelectionAction(Action a), której ActionListener będą powiadamiane przy każdej zmianie zaznaczenia. Za pomocą action.getSource() oraz action.getState() w metodzie performAction() można odczytać aktualnie zaznaczony węzeł lub powiązany z nim obiekt danych.
Poniższe metody dotyczą prezentacji drzewa w interfejsie użytkownika.
Metoda setRootVisible(boolean value) określa, czy węzeł główny ma być wyświetlany (domyślnie true). W przypadku wartości false widoczne są tylko węzły podrzędne.
Metoda setTopHandlesDisplayMode(boolean value) umożliwia wyświetlenie przy węzłach dodatkowego symbolu wskazującego, czy węzeł jest zwinięty lub rozwinięty oraz czy zawiera węzły podrzędne.
Metoda setVisibleRowCount(int newCount) określa preferowaną wysokość drzewa (właściwość preferredHeight) poprzez podanie liczby widocznych wierszy (węzłów).
Drzewo obsługuje zarówno przewijanie poziome, jak i pionowe, udostępniając odpowiednie metody konfiguracyjne (np. setVerticalScrollBarPolicy(int policy)).
TreeNode
Elementy (węzły) drzewa są reprezentowane przez obiekty klasy com.cisag.pgm.gui.TreeNode. Ich tworzenie odbywa się za pomocą konstruktora TreeNode(String text, String iconName, Object dataObject, String viewName, byte[] guid)
Parametr text jest wykorzystywany jako oznaczenie węzła.
Za pomocą parametru iconName można przypisać do węzła specjalną ikonę, przy czym należy podać ścieżkę do obiektu ikony z repozytorium.
Parametr dataObject umożliwia przypisanie do węzła dowolnego obiektu. Zazwyczaj jest to obiekt (lub referencja do niego), który dany węzeł reprezentuje.
Parametr viewName może zostać wykorzystany do przekazania fabryce TreeView (TreeViewCreator) informacji, w jaki sposób powiązany TreeView powinien zostać skonfigurowany.
Parametr guid służy do jednoznacznej identyfikacji węzła.
Właściwości przekazane jako parametry konstruktora są dostępne również poprzez odpowiednie metody get/set.
Podpowiedź (ToolTip) można zdefiniować za pomocą metody setTooltip(String text), natomiast identyfikator pomocy kontekstowej – za pomocą setContextHelpID(String id).
Węzeł tworzony przez konstruktor jest domyślnie elementem liściowym. Właściwość tę można zmienić metodą setAllowsChildren(boolean allow).
Aby rozszerzyć węzeł o węzły podrzędne, należy użyć metod:
- addChild(TreeNode child)
- addChild(TreeNode aheadOf, TreeNode child)
Podany węzeł zostaje dodany jako węzeł podrzędny, a jego referencja parent zostaje odpowiednio ustawiona. Można ją odczytać metodą getParent().
Jeżeli dodatkowo zostanie przekazany węzeł odniesienia w parametrze aheadOf, nowy węzeł zostanie wstawiony bezpośrednio przed wskazanym węzłem odniesienia.
Metoda removeChild(TreeNode child) usuwa wybrany węzeł podrzędny, natomiast removeChildren() usuwa wszystkie węzły podrzędne.
Poniższe metody służą do analizy węzła nadrzędnego:
- hasChildren() – zwraca wartość true, jeżeli istnieją węzły podrzędne
- getMaxDepth() – zwraca maksymalną głębokość hierarchii węzłów podrzędnych
- getChildren() – zwraca tablicę TreeNode[] zawierającą wszystkie bezpośrednie węzły podrzędne danego węzła
Metoda isAncestor(TreeNode node) sprawdza, czy wskazany węzeł występuje w łańcuchu węzłów nadrzędnych lub czy odpowiada bieżącemu węzłowi.
Metoda getLevel() analizuje poziom węzła w hierarchii względem elementu root.
Bezpośrednie sąsiednie węzły obiektu TreeNode można uzyskać za pomocą metod getNextNode() oraz getPreviousNode().
TreeView
Klasa com.cisag.pgm.gui.TreeView jest wykorzystywana w drzewach do wizualizacji węzłów i elementów liściowych. Klasa Tree nie rysuje elementów samodzielnie, lecz deleguje to zadanie do instancji TreeView.
TreeView pełni funkcję renderera dla obiektu TreeNode. O tym, która instancja TreeView zostanie użyta do renderowania danego TreeNode, decyduje TreeViewCreator, który działa jako fabryka instancji TreeView. Takie rozwiązanie umożliwia zdefiniowanie indywidualnej prezentacji dla każdego obiektu TreeNode.
Klasa udostępnia również implementację domyślną. W jej ramach węzły drzewa są wizualizowane za pomocą ikony i tekstu. Tekst jest pobierany bezpośrednio z obiektu TreeNode poprzez node.getText().
Ikona jest określana na podstawie node.getIconName(). Jeżeli dla węzła nie zdefiniowano własnej ikony, wykorzystywana jest ikona standardowa odpowiednia dla typu i stanu węzła (liść, folder otwarty lub zamknięty).
Implementacja domyślna uwzględnia również wizualizację węzłów zaznaczonych oraz znajdujących się w fokusie.
Do konstruktora TreeView(Tree owner) należy przekazać odpowiednią instancję Tree. Opcjonalnie można użyć konstruktora TreeView(Tree owner, int nodesize), aby określić rozmiar czcionki, korzystając ze stałych TreeView.SIZE_DEFAULT lub TreeView.SIZE_7PT.
Konstruktor domyślny (oznaczony jako protected) może być wykorzystywany przez podklasy, które nie potrzebują elementów UI implementacji domyślnej lub chcą zapobiec ich tworzeniu.
Zasadniczo zaleca się jednak korzystanie z implementacji domyślnej i ograniczenie się w podklasach do nadpisania metody dataToUi().
Mapowanie właściwości węzła na odpowiednie elementy UI (ikona, tekst) odbywa się w metodzie dataToUi(TreeNode node). Podklasy TreeView mogą nadpisać tę metodę, aby przypisać dane do własnych elementów UI i/lub uwzględnić dodatkowe właściwości węzła lub jego obiektu danych.
Dostęp do elementów implementacji domyślnej (tekst i ikona) możliwy jest poprzez chronione zmienne label oraz iconField. Zaleca się wywołanie na początku metody super.dataToUi(TreeNode node), aby kolory, obramowania i czcionki zostały ustawione zgodnie z implementacją domyślną.
EditableTreeView
Klasa com.cisag.pgm.gui.EditableTreeView jest wykorzystywana w drzewach do edycji węzłów i elementów liściowych, w szczególności do edycji nazwy węzła.
Klasa Tree nie edytuje elementów samodzielnie, lecz deleguje to zadanie do instancji EditableTreeView. EditableTreeView pełni funkcję edytora dla obiektu TreeNode. O tym, która instancja EditableTreeView zostanie użyta do edycji danego TreeNode, decyduje EditableTreeViewCreator, działający jako fabryka instancji EditableTreeView.
Analogicznie jak w przypadku TreeView (więcej informacji można znaleźć w rozdziale TreeView), konstrukcja ta umożliwia przypisanie specyficznego edytora do każdego obiektu TreeNode.
Klasa udostępnia również implementację domyślną. Różni się ona od implementacji TreeView jedynie tym, że do edycji tekstu używane jest pole tekstowe (TextField) zamiast etykiety (Label).
HTMLPane
Klasa com.cisag.pgm.gui.HTMLPane może być używana do integrowania treści HTML w interfejsie użytkownika.
Jako źródło treści HTML możliwe są następujące opcje:
- prosty tekst HTML
- adres URL, pod którym dostępna jest strona HTML, obraz lub dowolny dokument
- instancja com.cisag.pgm.gui.HTMLDocument
- instancja com.cisag.pgm.gui.ImageDocument
Pierwsza możliwość jest odpowiednia, gdy wymagane jest jedynie wyświetlenie sformatowanego tekstu. Tekst można ustawić bezpośrednio metodą setText(String text).
Należy zwrócić uwagę, że używane znaczniki HTML muszą być dozwolone wewnątrz elementu <DIV> (np. elementy <HTML>, <META>, <BODY>, itd. nie są tam dopuszczalne) oraz nie powinny być zależne od konkretnej przeglądarki.
Składnia elementów i atrybutów musi być poprawna, w tym prawidłowe parowanie znaczników otwierających i zamykających oraz odpowiednie pomijanie znaków specjalnych.
Należy unikać odwołań do innych dokumentów (elementy z atrybutami href lub src), jak również stosowania elementów formularzy (<FORM>, <INPUT> itd.) oraz skryptów.
Druga możliwość jest odpowiednia do osadzania dokumentów lub obrazów. Adresy URL powinny wykorzystywać protokół https.
Opcja ta jest typowo wykorzystywana do osadzania dokumentów z KnowledgeStore. Ustawienie adresu URL powinno odbywać się za pomocą metody setURL(String url). Nie zaleca się ustawiania adresu URL poprzez konstruktor HTMLPane(String), ponieważ ta sygnatura ma w przyszłości zostać wykorzystana do przypisania identyfikatora GUID.
Trzecia i czwarta możliwość są zasadniczo wariantami drugiej. Różnica polega na tym, że osadzany dokument nie jest referencjonowany przez URL, lecz znajduje się w pamięci operacyjnej jako tablica znaków (char[]) lub bajtów (byte[]) – np. gdy został wygenerowany lub obliczony w trakcie działania aplikacji.
Na podstawie tablicy znaków lub bajtów należy najpierw utworzyć instancję HTMLDocument lub ImageDocument, a następnie przekazać ją do HTMLPane za pomocą metody setDocument(HTMLDocument document).
Klasa ImageDocument powinna być używana zawsze wtedy, gdy dane reprezentują obraz (*.png, *.gif, .jpg itd.).
HTMLPane obsługuje w razie potrzeby przewijanie poziome i pionowe.
HTMLEditor
Klasa com.cisag.pgm.gui.HTMLEditor udostępnia prosty edytor tekstu dla sformatowanego tekstu (HTML).
Do edytora należy również pasek narzędzi z typowymi narzędziami, np. do wyboru kroju czcionki, rozmiaru czcionki, koloru oraz wyrównania tekstu.
Za pomocą konstruktora HTMLEditor(String guid) tworzony jest edytor z paskiem narzędzi. Metodami
setColorSupported(boolean supported) oraz setFontSupported(boolean supported) możliwe jest ukrycie określonych narzędzi lub elementów wyboru.
Dodatkowe elementy można wstawić do paska narzędzi za pomocą metody addCoolBarElement(VisualElement element, int side). Należy przy tym określić, po której stronie (Coolbar.LEFT lub Coolbar.RIGHT) paska ma zostać umieszczony nowy element. W przypadku wstawienia po lewej stronie automatycznie dodawany jest separator w postaci pionowej linii oddzielającej. Metodą removeCoolBarElement(VisualElement element) można ponownie usunąć wstawiony element.
Metoda setText(String text) umożliwia definiowanie treści po stronie serwera. Dla przekazanego tekstu HTML obowiązują te same zasady jak dla komponentu HTMLPane (więcej informacji można znaleźć w rozdziale HTMLPane). Metoda getText() zwraca aktualny tekst HTML edytora.
PopupMenu
Klasa com.cisag.pgm.gui.PopupMenu wyświetla listę pozycji menu w oknie typu popup. Zazwyczaj takie menu są używane jako menu kontekstowe i otwierane np. za pomocą prawego przycisku myszy.
Na poniższej ilustracji przedstawiono typowe menu kontekstowe dla komponentu EntityField.
Instancjonowanie obiektu PopupMenu odbywa się za pomocą konstruktora PopupMenu().
Do dodawania pozycji menu dostępne są różne metody add(). Zazwyczaj używana jest jednak metoda add(Action action), która na podstawie przekazanej akcji tworzy odpowiednią pozycję menu i przejmuje z akcji ikonę, tekst, ToolTip oraz bezpośrednią pomoc.
Pozycje menu, które nie powinny otrzymać ikony, mogą zostać dodane do menu za pomocą addTextMenuItem(Action action). Metoda addCheckBoxMenuItem(Action action) służy do dodawania pozycji menu ze znakiem wyboru. Linie oddzielające (separatory) można dodać metodą addSeparator().
Jeżeli nowe pozycje menu mają zostać wstawione nie na końcu, lecz w określonej pozycji, można użyć metod insert(int index, Action action) lub insertSeparator(int index). Metoda remove(int pos) usuwa określoną pozycję menu, natomiast removeAll() usuwa wszystkie pozycje menu.
Instancję Action powiązaną z określoną pozycją menu można ustalić za pomocą metody getAction(int pos). Analogicznie metoda getPosition(Action action) zwraca indeks określonej akcji. Za pomocą getSubactions() można pobrać wszystkie akcje w postaci tablicy.
Glue
Glue jest niewidocznym graficznym elementem pomocniczym, który służy do wypełniania wolnych przestrzeni w kontenerze, tak aby nie były one zajmowane przez inne elementy.
Glue jest stosowany głównie w kontenerach z BoxLayout (więcej informacji można znaleźć w rozdziale BoxLayout). Jeżeli w danym kontenerze dostępne jest więcej miejsca, niż wynika to z preferredWidth lub preferredHeight, BoxLayout rozdziela nadmiarową przestrzeń pomiędzy elementy, o ile ich maximumWidth lub maximumHeight na to pozwala.
Jeżeli przestrzeń ma zostać utworzona w określonym miejscu (np. na lewym brzegu kontenera lub pośrodku między dwoma elementami), w to miejsce można wstawić Glue. Wówczas Glue zajmuje całą dostępną przestrzeń, a wartości maximumWidth lub maximumHeight pozostałych elementów są ograniczane do ich preferredWidth lub preferredHeight, ponieważ w przeciwnym razie wolna przestrzeń zostałaby rozdzielona również pomiędzy te elementy.
Działanie Glue wynika wyłącznie z faktu, że jego właściwości minimumWidth, minimumHeight, preferredWidth i preferredHeight mają wartość 0, natomiast przynajmniej jedna z właściwości maximumWidth lub maximumHeight ma bardzo dużą wartość (Short.MAX_VALUE).
Za pomocą konstruktora Glue(int type) określa się kierunek, w którym element ma się rozszerzać. Możliwe są wartości Glue.HORIZONTAL, Glue.VERTICAL lub Glue.BOTH. Bez podania tego parametru (konstruktor domyślny) tworzony jest obiekt 2-wymiarowy. Glue stosuje się w większości przypadków poziomo. Ponieważ Glue zawsze rozszerza się zgodnie z kierunkiem BoxLayout, powinien być używany wyłącznie w tym samym kierunku.
Przykład zastosowania Glue znajduje się w rozdziale BoxLayout.
Strut
Strut jest niewidocznym graficznym elementem pomocniczym o zdefiniowanym rozmiarze w kierunku poziomym lub pionowym.
Podobnie jak Glue, również Strut jest stosowany głównie w kontenerach z menedżerem układu BoxLayout (więcej informacji można znaleźć w rozdziale BoxLayout) i może być używany jako element dystansowy.
Działanie Strut polega na tym, że w zależności od kierunku właściwości minimumWidth, preferredWidth oraz maximumWidth albo właściwości minimumHeight, preferredHeight i maximumHeight przyjmują żądaną wartość odstępu. W drugim kierunku wartości minimalne i preferowane wynoszą 0, a wartość maksymalna to Short.MAX_VALUE.
W konstruktorze Strut(int type) należy określić żądany kierunek. Możliwe są wartości Strut.HORIZONTAL lub Strut.VERTICAL. Kierunek Strut powinien zawsze odpowiadać kierunkowi kontenera (tzn. BoxLayout). Domyślną szerokością lub wysokością jest 5 pikseli. Jeżeli potrzebna jest inna szerokość lub wysokość, można ją określić za pomocą konstruktora Strut(int type, int size).
Przykład zastosowania Strut znajduje się w rozdziale BoxLayout.
Layout Manager
Rozmieszczenie elementów odbywa się za pomocą menedżerów układu na poziomie kontenerów (VisualElement). Każdemu kontenerowi w hierarchii kontenerów należy przypisać własną instancję menedżera układu, która przejmuje zarządzanie rozmieszczeniem elementów w tym kontenerze. Jeżeli element podrzędny jest jednocześnie kontenerem, również wymaga własnego menedżera układu.
W pakiecie com.cisag.pgm.gui dostępne są następujące menedżery układu:
- com.cisag.pgm.gui.BorderLayout
- com.cisag.pgm.gui.BoxLayout
- com.cisag.pgm.gui.FlowLayout
- com.cisag.pgm.gui.StandardLayout
- com.cisag.pgm.gui.XYLayout
W aplikacjach Comarch ERP Enterprise stosowane są głównie:
- StandardLayout,
- BoxLayout
- BorderLayout.
Każdy z wymienionych menedżerów układu wykorzystuje typową dla siebie strategię rozmieszczania elementów. Strategia ta dotyczy zarówno logicznego pozycjonowania, tzn. względnego rozmieszczenia elementów względem siebie, jak i faktycznego umiejscowienia oraz rozmieszczenia elementów. Menedżer układu jest automatycznie dostosowywany przy zmianie rozmiaru okna lub innych właściwości istotnych dla układu (np. preferowanego rozmiaru) elementu.
Ogólnie rzecz biorąc, menedżer układu wykorzystuje następujące informacje do pozycjonowania i ustalania rozmiaru elementu:
- aktualny rozmiar kontenera,
- szerokość obramowania lub marginesy w kontenerze,
- pozycję (indeks) danego elementu w kontenerze,
- Constraints użyte przy metodzie add(),
- właściwości minimumWidth / minimumHeight, preferredWidth / preferredHeight, maximumWidth / maximumHeight elementu,
- właściwości alignmentX / alignmentY elementu.
W zależności od strategii niektóre menedżery układu traktują część tych informacji jedynie jako życzenie, ponieważ ze względu na ograniczenia innych elementów nie zawsze może ono zostać spełnione lub może zostać spełnione jedynie w przybliżeniu.
Niektóre menedżery układu posiadają również własne właściwości, które mają wpływ na układ, np.:
- kierunek (poziomy, pionowy),
- odstępy między elementami,
- odstęp między wierszami.
StandardLayout (więcej informacji można znaleźć w rozdziale StandardLayout) wykorzystuje ponadto (ponad poziomem pojedynczego kontenera) linie prowadzące (kolumny) do wyrównywania elementów.
Kolejnym zadaniem menedżera układu jest uwzględnianie właściwości minimumWidth / minimumHeight, preferredWidth / preferredHeight, maximumWidth / maximumHeight oraz alignmentX / alignmentY kontenera. Kontener nie może samodzielnie obliczyć tych wartości, ponieważ zależą one od strategii danego menedżera układu.
Ponieważ dostępne są różne układy, programista aplikacji ma praktycznie nieograniczone możliwości rozmieszczania elementów zgodnie z własną koncepcją.
Constraints
Constraints dostarczają menedżerowi układu dodatkowych informacji o tym, gdzie lub w jaki sposób element ma zostać pozycjonowany. To, czy menedżer układu wymaga Constraints oraz w jakiej postaci muszą one zostać przekazane, zależy od danego menedżera układu. BoxLayout i zagnieżdżony BoxLayout nie wymagają żadnych Constraints, natomiast BorderLayout oczekuje określenia pozycji w postaci stałych BorderLayout.CENTER, BorderLayout.NORTH itd. StandardLayout zazwyczaj wymaga jedynie podania numeru kolumny, dopuszcza jednak również użycie specjalnego obiektu parametru (com.cisag.pgm.gui.ReferenceConstraints).
Specyfikacja lub przekazanie Constraints dla elementu następuje podczas jego dodawania do kontenera (jako dodatkowy parametr metody add()). Decydujące jest przy tym, aby do kontenera został wcześniej przypisany odpowiedni menedżer układu.
StandardLayout
StandardLayout (com.cisag.pgm.gui.StandardLayout) jest, jak sama nazwa wskazuje, najczęściej stosowanym menedżerem układu w aplikacjach Comarch ERP Enterprise. Jego strategia układu została zaprojektowana specjalnie do jedno- i wielokolumnowego rozmieszczania pól (wraz z etykietami).
W kierunku pionowym menedżer układu pracuje z miarą wysokości wiersza (właściwość rowHeight), do której elementy są wyrównywane. Elementom zasadniczo przypisywana jest ich preferowana wysokość (preferredHeight). Punkt początkowy następnego wiersza wynika następnie z najwyższego elementu w bieżącym wierszu, przy czym ten punkt początkowy jest dodatkowo dopasowywany do rastra wiersza (najbliższa wartość podzielna przez rowHeight).
Wyrównanie poziome odbywa się za pomocą linii prowadzących (guides). Definicja linii prowadzących jest stosowana przez wszystkie elementy kierowane przez StandardLayout i zasadniczo jest niezależna od kontenera. Linie prowadzące nie powinny być definiowane dla pojedynczego, logicznie oddzielonego kontenera podrzędnego. Linie prowadzące są jednak dostępne dla każdego podkontenera (rekurencyjnie), niezależnie od tego, czy w tym podkontenerze zdefiniowano własny menedżer układu. Nowa definicja linii prowadzących ma sens tylko wtedy, gdy podkontener faktycznie wymaga innych linii prowadzących niż te odziedziczone, np. gdy mają być stosowane podziały kolumn od 2. kolumny.
Definicja linii prowadzących odbywa się poprzez zdefiniowanie kolumn, przy czym dla każdej kolumny można określić do 6 linii prowadzących:
- początek kolumny
- początek etykiety
- koniec etykiety
- początek pola
- koniec pola
- koniec kolumny
Definiowanie kolumn lub linii prowadzących powinno zasadniczo następować poprzez metodę setGuides() kontenera VisualElement, gdzie przekazywana jest lista obiektów ListColumnWidth. W najprostszym przypadku wystarczy przekazać żądaną liczbę kolumn. Możliwe jest jednak również precyzyjne definiowanie szerokości poszczególnych kolumn lub nawet odstępów między kolumnami.
Poniższa ilustracja przedstawia GroupBox z StandardLayout. W GroupBox znajduje się jedno pole oraz pole typu Subshell z pięcioma polami. Subshell również posiada StandardLayout jako menedżera układu. Pola w GroupBox oraz w Subshell (chociaż znajdują się w różnych kontenerach) są wyrównane do tych samych linii prowadzących.
Do utworzenia instancji StandardLayout należy zawsze używać wyłącznie konstruktora domyślnego (bez parametrów). Po przypisaniu tej instancji do kontenera (np. za pomocą setLayout()) jako menedżera układu, do kontenera można dodawać elementy podrzędne.
W metodzie add() jako Constraints należy przekazać żądany numer kolumny. Numery kolumn są liczone od 0 do n-1 i przekazywane jako ciąg znaków (String). Poniższy przykład dodaje pole do drugiej kolumny:
Jeżeli element ma rozciągać się na więcej niż jedną kolumnę, należy jako Constraints przekazać numer kolumny początkowej i końcowej, oddzielone myślnikiem. Poniższy przykład dodaje pole do pierwszych dwóch kolumn:
Poniższa tabela przedstawia możliwe Constraints:
| Constraints | Działanie |
| i | Element jest wstawiany do kolumny i, przy czym obowiązuje: 0 <= i < N |
| i-j | Element jest wstawiany do kolumn od i do j, przy czym obowiązuje: 0 <= i <= j < N |
| i- | Element jest wstawiany do kolumn od i do N-1, przy czym obowiązuje: 0 <= i < N |
| i:k | Element jest wstawiany do kolumny i na k kolumn, tzn. do kolumn od i do i+k-1, przy czym obowiązuje: 0 <= i <= i+k-1 < N |
| SPAN_ALL_COLUMNS | Element rozciąga się na wszystkie kolumny |
Definiowanie kolumn
Chociaż StandardLayout umożliwia definiowanie linii prowadzących za pomocą odpowiednich konstruktorów, nie należy z tej możliwości korzystać. Zamiast tego należy używać metod setGuides() kontenera VisualElementContainer.
Przy użyciu metody
setGuides(double[] labelWidth, double[] fieldWidth, double[] leftMargins, double[] rightMargins)
można definiować linie prowadzące poprzez określenie szerokości lub odstępów.
Tablica labelWidth definiuje szerokości obszaru etykiet dla kolumn. Tablica fieldWidth definiuje szerokości obszaru pól dla kolumn. Wartości muszą być zawsze podawane jako wartości względne do łącznej dostępnej szerokości. Jeżeli przykładowo zdefiniowano 2 kolumny, a etykiety mają zajmować 30% dostępnej szerokości kolumny, obie tablice powinny zostać obliczone w następujący sposób:
double wLabel = wColumn * 0.3; // 30 percent
double wField = wColumn – wLabel;
double[] labelWidth = {wLabel, wLabel};
double[] fieldWidth = {wField, wField};
Suma wszystkich szerokości powinna zawsze wynosić 1, przy czym uwzględniane są wyłącznie szerokości obszaru etykiet i pól, bez odstępów (marginesów).
Za pomocą tablic leftMargins oraz rightMargins można dodatkowo zdefiniować odstępy po lewej i prawej stronie kolumny. Wartości w tym przypadku podawane są w pikselach.
Ustawianie dodatkowych właściwości
Właściwość stretchElementsToColumnWidth ma domyślnie wartość true. Powoduje to, że wszystkie elementy — niezależnie od ich zdefiniowanej maksymalnej szerokości (getMaximumWidth()) — są rozszerzane do szerokości kolumny. Jeżeli właściwość ta zostanie ustawiona na false, wówczas uwzględniane są odpowiednie maksymalne szerokości.
Metoda setRowHeight(int rowHeight) umożliwia zdefiniowanie wysokości wiersza. Wartość domyślna wynosi 20 pikseli.
Za pomocą metody setVGap(int vgap) można wstawić dodatkowy odstęp między wierszami. Metoda getVGap() zwraca ten odstęp. Wartość domyślna wynosi 0.
Przykład zastosowania StandardLayout można znaleźć w rozdziale StandardLayout.
ReferenceConstraints
Klasa com.cisag.pgm.gui.ReferenceConstraints służy jako obiekt Constraints dla StandardLayout. Podczas gdy proste Constraints oparte na ciągach znaków zawsze odnoszą się do jednej lub kilku kolumn, przy użyciu ReferenceConstraints można realizować również bardziej złożone układy. Dzięki temu możliwe jest np. umieszczenie 2 pól obok siebie w tej samej kolumnie (z odstępem 2 pikseli):
new ReferenceConstraints(field,
ReferenceConstraints.RIGHT_OF,
2));
BorderLayout
BoxLayout nie wymaga Constraints, lecz pracuje wyłącznie na podstawie właściwości elementów (minimumWidth / minimumHeight, preferredWidth / preferredHeight, maximumWidth / maximumHeight oraz alignmentX / alignmentY), które są interpretowane zgodnie z wybranym kierunkiem.
W wybranym kierunku głównym elementy są rozmieszczane obok siebie (lub jeden pod drugim). Zazwyczaj elementy otrzymują przy tym swoją preferredWidth (lub preferredHeight). Jeżeli w kontenerze dostępne jest więcej miejsca, niż elementy potrzebują zgodnie z ich preferredWidth (lub preferredHeight), powiększane są te elementy, których maximumWidth (lub maximumHeight) na to pozwala. Jeżeli miejsce nadal pozostaje wolne, jest ono rozdzielane między te elementy proporcjonalnie, z uwzględnieniem ich maksymalnego rozmiaru. Jeżeli kontener nie jest wystarczająco duży, aby wyświetlić wszystkie elementy zgodnie z ich preferredWidth (lub preferredHeight), elementy są zmniejszane, o ile pozwala na to ich minimumWidth (lub minimumHeight). Jeżeli trzeba zmniejszyć kilka elementów, są one zmniejszane proporcjonalnie, z uwzględnieniem ich minimalnego rozmiaru. Jeżeli elementów nie da się już wystarczająco zmniejszyć, zostają one ewentualnie przycięte przez granice kontenera.
BoxLayout rozmieszcza elementy nie tylko w wybranym kierunku głównym, lecz także w drugim wymiarze. Punkty odniesienia definiowane przez właściwość alignmentY (lub alignmentX) określają (względny) punkt odniesienia elementu w kierunku pionowym (lub poziomym), względem którego następuje wyrównanie. W odniesieniu do dopasowania do wysokości (lub szerokości) kontenera obowiązują podobne zasady jak dla kierunku głównego, tzn. element jest powiększany lub zmniejszany wyłącznie w zakresie dopuszczalnym przez maximumHeight / minimumHeight (lub maximumWidth / minimumWidth). Fakt, że element zwraca w maximumHeight (lub maximumWidth) wartość Short.MAX_VALUE, nie oznacza automatycznie, że wypełni on całą wysokość (lub szerokość) kontenera. W tym miejscu również właściwość alignmentY (lub alignmentX) odgrywa rolę, ponieważ określa względne wyrównanie względem innych elementów, mimo że ich rozmiar został zmieniony.
W większości zastosowań zaleca się, aby właściwość alignmentY (lub alignmentX) wszystkich elementów w kontenerze miała tę samą wartość. Mieszanie różnych wartości (np. elementów wyrównanych do lewej i wyśrodkowanych) jest co prawda możliwe, lecz zazwyczaj nie jest zalecane, ponieważ różne ustawienia elementów mogą prowadzić do nieoczekiwanych rezultatów.
FlowLayout
W przypadku klasy com.cisag.pgm.gui.FlowLayout chodzi o prosty menedżer układu, który rozmieszcza elementy kontenera w wierszach. Elementy otrzymują przy tym swoją preferowaną szerokość, a menedżer układu automatycznie rozpoczyna nowy wiersz, gdy bieżący wiersz jest pełny.
W aplikacjach Comarch ERP Enterprise ten menedżer układu praktycznie nie jest używany.
XYLayout, XYConstraints
Klasa com.cisag.pgm.gui.XYLayout jest przeznaczona do przypadków, w których elementy kontenera są rozmieszczane ręcznie. Klasa ta działa podobnie jak java.awt.LayoutManager, jednak nie stosuje strategii układu, lecz umożliwia ustawianie współrzędnych oraz rozmiarów elementów określonych za pomocą XYConstraints.
Tego rodzaju podejście nadaje się wyłącznie do zastosowań specjalnych, ponieważ układy kontenerów i elementów nie dostosowują się do rozdzielczości ekranu ani do rozmiarów czcionek. W aplikacjach Comarch ERP Enterprise ten menedżer układu praktycznie nie jest używany.
Komponenty graficzne
Border
Wszystkie elementy (więcej informacji można znaleźć w rozdziale VisualElement) mogą być wyposażone w ramkę (VisualElement#setBorder(Border)). Niektóre elementy, takie jak np. pola, przyciski i GroupBox, posiadają już ramkę i nie można jej zmienić (w celu zapewnienia jednolitego wyglądu). Świadome stosowanie ramek jest w aplikacjach Comarch ERP Enterprise używane zazwyczaj tylko w listach, aby oddzielić wiersze od siebie.
W pakiecie com.cisag.pgm.gui dostępne są różne implementacje interfejsu com.cisag.pgm.gui.Border:
- com.cisag.pgm.gui.CompoundBorder
- com.cisag.pgm.gui.DecoratedBorder
- com.cisag.pgm.gui.EmptyBorder
- com.cisag.pgm.gui.LineBorder
Poniższe klasy są przestarzałe lub nie odpowiadają stylowi Comarch ERP Enterprise i nie powinny być używane:
- com.cisag.pgm.gui.EtchedBorder
- com.cisag.pgm.gui.LoweredBevelBorder
- com.cisag.pgm.gui.RaisedBevelBorder
- com.cisag.pgm.gui.RidgeBorder
LineBorder
Klasa com.cisag.pgm.gui.LineBorder może być używana do nadania elementowi jednokolorowej, ciągłej linii obramowania. W zależności od wybranego konstruktora można określić kolor i grubość linii. W razie potrzeby grubość można ustawić osobno dla każdej z czterech stron. Wartość 0 oznacza, że po danej stronie nie jest rysowana żadna linia.
DecoratedBorder
Klasa com.cisag.pgm.gui.DecoratedBorder oferuje różne style obramowania. Dodatkowo można wybrać kolor oraz grubość ramki. Ramka jest stosowana do wszystkich czterech stron elementu. Istnieje możliwość złożenia ramki z kilku DecoratedBorder przy użyciu klasy CompoundBorder.
CompoundBorder
Za pomocą klasy com.cisag.pgm.gui.CompoundBorder możliwe jest złożenie ramki z innych ramek. Każdej stronie ramki przypisywana jest przy tym instancja DecoratedBorder. Dzięki temu można np. zastosować dla każdej strony inny styl, inny kolor i/lub inną grubość. Zamiast instancji DecoratedBorder można również użyć wartości null. W takim przypadku po danej stronie nie zostanie dodane obramowanie.
EmptyBorder
Klasa com.cisag.pgm.gui.EmptyBorder zachowuje się podobnie jak klasa LineBorder, z tą różnicą, że linia pozostaje niewidoczna (transparentna). Dzięki temu reprezentowany jest jedynie efekt najbardziej zewnętrznego marginesu elementu (więcej informacji można znaleźć w rozdziale VisualElement). Ze względu na przejrzystość w tym celu powinna być jednak stosowana metoda setMargins().
Color
Klasa com.cisag.pgm.gui.Color służy do reprezentacji wartości kolorów, np. do ustawiania koloru pierwszego planu lub koloru tła elementu (więcej informacji można znaleźć w rozdziale VisualElement).
Dla kolorów stosowany jest model barw RGBA, tzn. każdy kolor jest określony przez cztery komponenty. Trzy z tych komponentów odpowiadają trzem podstawowym kolorom (red – czerwony, green – zielony i blue – niebieski). Czwarta komponenta (A lub alpha) określa nieprzezroczystość lub przezroczystość koloru. Zakres wartości czterech komponentów mieści się między 0 a 255 (włącznie), przy czym wartość 255 oznacza 100% nieprzezroczystości.
Półprzezroczyste kolory nie są obecnie obsługiwane. Jedynym wyjątkiem jest color.TRANSPARENT. Za pomocą setBackColor(color.TRANSPARENT) można spowodować, że standardowy kolor tła elementu zostanie usunięty, tak aby elementowi została przypisana barwa tła kontenera.
Elementy posiadają w odniesieniu do kolorów ustawienie wstępne, które można zmienić w ustawieniach użytkownika (sekcja Internet). W ten sposób wartości te automatycznie dopasowują się np. do motywu. Jawne ustawianie kolorów jest jednak dopuszczalne, jeżeli chodzi o elementy Look & Feel, takie jak tło przycisków. W tym celu dostępnych jest wiele standardowych kolorów, np. stałe Color.BLACK, Color.WHITE, Color.RED, Color.GREEN, Color.BLUE oraz Color.TRANSPARENT.
Klasa com.cisag.pgm.gui.UIManager (więcej informacji można znaleźć w rozdziale UIManager) umożliwia dostęp do kolorów zależnych od Look & Feel.
Font
Klasa com.cisag.pgm.gui.Font służy do reprezentacji czcionek i właściwości czcionek. Za pomocą metody VisualElement.setFont(Font) (więcej informacji można znaleźć w rozdziale VisualElement) można elementowi przypisać określoną czcionkę lub określony styl czcionki.
Klasa com.cisag.pgm.gui.Font udostępnia dwie statyczne metody (metody fabryczne) do tworzenia obiektów czcionki:
- getFont(int style, int size)
- getFont(String family, int style, int size)
Druga metoda nie powinna być stosowana, ponieważ jawne określenie czcionki może powodować problemy: z jednej strony jest ona nadpisywana przez ustawienia użytkownika, z drugiej strony po stronie klienta nie można zagwarantować, że wskazana czcionka jest rzeczywiście dostępna.
Przy pierwszej metodzie nie należy określać możliwie najmniejszego rozmiaru czcionki, lecz wartość określoną przez Font.DEFAULT_SIZE. W ten sposób elementy otrzymują odpowiednią wartość wynikającą z ustawień Look & Feel lub z ustawień użytkownika.
Dla wyboru stylu czcionki lub atrybutów czcionki dostępne są następujące stałe:
- Font.PLAIN
- Font.BOLD
- Font.ITALIC
- Font.UNDERLINE
- Font.STRIKETHROUGH
- Font.SUBSCRIPT
Stałe można z reguły łączyć ze sobą (bitowe OR). Jednoczesne użycie Font.SUBSCRIPT i Font.SUPERSCRIPT nie jest możliwe. Również Font.PLAIN nie może być stosowane razem z Font.BOLD lub Font.ITALIC.
Insets
Klasa com.cisag.pgm.gui.Insets służy do określania ramek lub odstępów krawędziowych w elementach (więcej informacji można znaleźć w rozdziałach Odstępy, VisualElement oraz Border).
Odstępy krawędziowe można ustawiać dla wszystkich elementów (com.cisag.pgm.gui.VisualElement) za pomocą metody setMargin(Insets) lub odczytywać za pomocą metody getMargin(). Wszystkie elementy posiadają domyślne odstępy krawędziowe, które są zazwyczaj dostosowane do odpowiedniego rozmiaru czcionki.
Do utworzenia obiektu Insets można użyć konstruktora Insets(int top, int left, int bottom, int right). Dla każdej z czterech stron należy przy tym podać żądaną wartość odstępu jako liczbę pikseli.
Zaleca się jednak stosowanie do tworzenia Insets klasy com.cisag.pgm.gui.UIManager (więcej informacji można znaleźć w rozdziale UIManager), ponieważ uwzględnia ona zarówno aktualny Look & Feel (motyw), jak i rozmiar czcionki oraz rozdzielczość ekranu.
Przykładowo poniższy fragment kodu dostarcza standardowe Insets dla widoków kontenerów:
UIManager.getUIManager().getInsets(„Insets.all”, VisualElement.this)
Dzięki podaniu elementu UIManager może uwzględnić właściwości specyficzne dla elementu, takie jak np. rozmiar czcionki. Parametr „Insets.all” służy jako klucz dla żądanego rodzaju Insets (w tym przypadku: na wszystkich czterech stronach). Odpowiednie klucze są zdefiniowane także dla innych rodzajów Insets.
Icon
lasa com.cisag.pgm.gui.Icon służy do reprezentacji elementów graficznych, piktogramów. Ikony są zapisywane w repozytorium i zazwyczaj określane za pomocą dwóch właściwości (miejsce zapisu, rozmiar) przekazywanych jako obiekt klasy com.cisag.pgm.gui.Icon.
Wyświetlanie ikon odbywa się poprzez odpowiedni element (np. Label, Button, IconField), który przekazuje wymagane informacje (miejsce zapisu, rozmiar) za pomocą obiektu klasy com.cisag.pgm.gui.Icon.
Zazwyczaj instancje Icon są tworzone za pomocą statycznej metody Icon.getIcon(String path) lub Icon.getIcon(String path, int w, int h) w klasie Metadata (Entwicklungsobjekt Icon). Przy użyciu statycznej metody Icon.getIcon(String path, int w, int h) zapisana wielkość w repozytorium może zostać nadpisana.
UIManager
ActionList
Klasa com.cisag.app.gui.ActionList umożliwia łączenie wielu Actions w jednym obiekcie. Taki obiekt ActionList może zostać wykorzystany np. do utworzenia przycisku menu (MenuButton).
Przykłady zastosowań
W tym rozdziale przedstawiono kilka przykładów typowych zadań GUI. W przykładach wykorzystywane są częściowo klasy i obiekty biznesowe, takie jak rzeczywiste aplikacje Comarch ERP Enterprise, jednak są one od nich całkowicie niezależne.
Zastosowanie menedżerów układu
StandardLayout
Poniższy przykład pokazuje, jak można zdefiniować wielokolumnowe układy (Layouts) dla elementów (pól) na całkowicie pustej powierzchni. Podstawą takiego układu jest menedżer układu StandardLayout-Layout-Manager (więcej informacji można znaleźć w rozdziale StandardLayout). Menedżer ten kieruje się liniami prowadzącymi dla odpowiednich zakresów (w tym przypadku osi y). Po utworzeniu struktury linie prowadzące są dziedziczone przez nadrzędny kontener, który je definiuje. Jeśli elementy mają być rozmieszczone na kilku kontenerach według wspólnych linii, linie te powinny zostać zdefiniowane tylko raz.
W przykładzie linie prowadzące dla kolumn są zdefiniowane w zewnętrznym kontenerze. Wykorzystanie odbywa się poprzez setGuides().
Przykład pokazuje ponadto:
- przypisywanie elementów do jednej lub kilku kolumn
- wykorzystywanie metadanych do tworzenia pól
- wykorzystywanie ścieżek względnych dla metadanych (ścieżka bazowa na poziomie kontenera)
- wybór trybu wyboru na poziomie kontenera dla wszystkich pól
- kolejność dotycząca tworzenia, dodawania i konfigurowania elementów
- wykorzystanie Subshelfs
…
import com.cisag.app.general.gui.ItemField;
…
import com.cisag.pgm.gui.BooleanField;
import com.cisag.pgm.gui.Shelf;
import com.cisag.pgm.gui.StandardLayout;
import com.cisag.pgm.gui.TextField;
import com.cisag.pgm.gui.ValueSetField;
import com.cisag.pgm.gui.View;
…
public class ExampleApplication extends CisUiApplication {
…
private ItemField number;
private TextField description;
private ValueSetField type;
private ValueSetField materialType;
private ValueSetField status;
private TextField ean;
private BooleanField purchaseItem;
private BooleanField inventoryItem;
private BooleanField salesItem;
private BooleanField productionItem;
…
/**
* utworzenie obszaru identyfikacyjnego.
*/
protected void createIdentArea() {
View pane = getIdentPane();
// zdefiniowanie dwukolumnowego układu dla całego obszaru identyfikacyjnego
pane.setGuides(2);
pane.setLayout(new StandardLayout());
// zdefiniowanie ścieżki bazowej dla metadanych
pane.setPath(„com.cisag.app.general.obj.item:”);
// wybór trybu wyboru dla wszystkich pól
pane.setSelectionView(true);
// tworzenie pól (ścieżki względne dla metadanych)
number = new ItemField(Guid.AUTOGUID, „.number”, true);
description = new TextField(Guid.AUTOGUID, „.description”);
type = new ValueSetField(Guid.AUTOGUID, „.type”);
materialType = new ValueSetField(Guid.AUTOGUID, „.materialType”);
status = new ValueSetField(Guid.AUTOGUID, „.status”);
ean = new TextField(Guid.AUTOGUID, „.ean”);
purchaseItem = new BooleanField(Guid.AUTOGUID, „.purchaseItem”);
inventoryItem = new BooleanField(Guid.AUTOGUID, „.inventoryItem”);
salesItem = new BooleanField(Guid.AUTOGUID, „.salesItem”);
productionItem= new BooleanField(Guid.AUTOGUID, „.productionItem”);
// dodawanie pól
// pierwszy wiersz
pane.add(number, „0”); // kolumna 1
pane.add(description, „1”); // kolumna 2
// drugi wiersz
pane.add(type, „0”); // kolumna 1
pane.add(materialType, „1”); // kolumna 2
// utworzenie (sub-)Shelf dla kolejnych pól/wierszy
Shelf subshelf = new Shelf(Guid.AUTOGUID);
// StandardLayout jako Default-Action dla struktury Shelf
subshelf.setLayout(new StandardLayout());
// dodanie Shelf do kontenera, aby wykorzystać wszystkie kolumny
pane.add(subshelf, StandardLayout.SPAN_ALL_COLUMNS);
// wybór trybu wyboru również dla wszystkich pól struktury Shelf
subshelf.setSelectionView(true);
// określenie, że Shelf ma być „zwinięty” przy starcie
subshelf.setCollapsed(true);
// dodanie poniższych pól/wierszy do Shelf
// trzeci wiersz
subshelf.add(status, „0”);
subshelf.add(ean, „1”);
// czwarty wiersz
…
subshelf.add(purchaseItem, „0”);
subshelf.add(inventoryItem, „1”);
// piąty wiersz
subshelf.add(salesItem, „0”);
subshelf.add(productionItem,”1″);
…
}
…
}
BoxLayout
Przykład przedstawia zastosowanie i sposób działania menedżera układu BoxLayout.
W przykładzie wykorzystano dwie instancje BoxLayout. Pierwsza instancja znajduje się w Content-Pane okna dialogowego i rozmieszcza jego elementy pionowo. W dolnej części Content-Pane znajduje się View, który zawiera dwa przyciski [OK] i [Anuluj]. Do tego View przypisano BoxLayout z orientacją poziomą.
Aby uzyskać niewielki odstęp między elementami, w przykładzie wykorzystano Struts (więcej informacji można znaleźć w rozdziale Strut). W podkontenerze z dwoma przyciskami dodatkowo zastosowano (poziomy) Glue (więcej informacji można znaleźć w rozdziale Glue), aby przesunąć przyciski w prawo.
W przypadku Label oraz pola tekstowego (MultiLineTextField) konieczna jest zmiana wyrównania poziomego za pomocą setAlignmentX (VisualElement.CENTER_ALIGNMENT), ponieważ w tym miejscu wartości domyślne są ustawione na VisualElement.LEFT_ALIGNMENT, co nie jest spójne z pozostałymi elementami.
Wywołanie setMaximumWidth(Short.MAX_VALUE) powoduje, że Label i pole tekstowe mogą zostać poszerzone. Dodatkowo, poprzez wywołanie setMinimumHeight(0) oraz setMaximumHeight(Short.MAX_VALUE) dla pola tekstowego określono, że element ten może przyjmować dowolną wysokość w pionie.
Dla podkontenera, poprzez setMaximumHeight(0), określono, że może on przyjmować jedynie swoją preferowaną wysokość (preferredHeight). Dla znajdujących się w nim przycisków za pomocą setMinimumWidth(10) zdefiniowano, że w razie potrzeby mogą zostać zmniejszone do 10 pikseli. Natomiast dla maximumWidth nie ustawiono żadnej wartości, dzięki czemu przyciski nie mogą zostać poszerzone ponad swoją preferowaną szerokość. Jeśli dostępna jest większa ilość miejsca, zostaje ona przydzielona elementowi Glue (niewidocznemu), co powoduje przesunięcie przycisków do prawej krawędzi.
…
dialog = new Dialog(…);
…
View contentPane = dialog.getContentPane();
contentPane.setLayout(new BoxLayout(BoxLayout.Y_AXIS));
Label prompt = new Label(Guid.AUTOGUID, …); // „Komentarz:”
// …
contentPane.add(prompt);
prompt.setAlignmentX(Label.CENTER_ALIGNMENT);
prompt.setMinimumWidth(0);
prompt.setMaximumWidth(Short.MAX_VALUE);
contentPane.add(new Strut(Strut.VERTICAL, 5));
MultiLineTextField text = new MultiLineTextField(Guid.AUTOGUID, …);
contentPane.add(text);
text.setLabel(null);
text.setAlignmentX(MultiLineTextField.CENTER_ALIGNMENT);
text.setMinimumWidth(0);
text.setMaximumWidth(Short.MAX_VALUE);
text.setMinimumHeight(0);
text.setMaximumHeight(Short.MAX_VALUE);
contentPane.add(new Strut(Strut.VERTICAL, 5));
View buttonPanel = new View(new BoxLayout(BoxLayout.X_AXIS));
contentPane.add(buttonPanel);
buttonPanel.setMaximumHeight(0); // -> preferredHeight
Action okAction = new Action(OK, „com.cisag.pgm.gui.ButtonBarOK”, …);
okAction.setDefaultAction(true);
Button okButton = new Button(okAction, Button.TEXT_BUTTON);
buttonPanel.add(okButton);
okButton.setMinimumWidth(10);
buttonPanel.add(new Strut(Strut.HORIZONTAL, 5));
Action cancelAction = new Action(CANCEL, „com.cisag.pgm.gui.ButtonBarCancel”, …);
Button cancelButton = new Button(cancelAction, Button.TEXT_BUTTON);
buttonPanel.add(cancelButton);
cancelButton.setMinimumWidth(10);
…
Zastosowanie przycisków
MenuButton



