Rozwój interfejsu

Spis treści

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:

public Object getValueAsObject()
public void setValueFromObject(Object)
Data description

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.

Wskazówka
W przypadku wielojęzycznego pola tekstowego (tzn. z obsługą NLS-Support) pozycja oraz funkcjonalność przycisku dialogowego są wykorzystywane do wprowadzania tłumaczeń.

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.

Uwaga
Dopóki okno jest używane, program musi przechowywać do niego referencję. W przeciwnym razie istnieje ryzyko przedwczesnego usunięcia przez 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.

Uwaga
Do wyświetlania komunikatów oraz dialogów potwierdzenia należy stosować klasę com.cisag.pgm.gui.ConfirmationDialog.

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:

  • element otwierający służy obszarowi jako punkt odniesienia do rozmieszczenia dialogu na ekranie,
  • wymagany tytuł dialogu jest już ustalany podczas jego tworzenia na podstawie repozytorium.

Uwaga
Podanie właściwego okna nadrzędnego jest istotne (więcej informacji można znaleźć w rozdziale Kontenery najwyższego poziomu). Z tego względu użycie CisUiApplication#getWindow() lub ApplicationWindow.currentWindow() jest dopuszczalne wyłącznie wtedy, gdy można zagwarantować, że wywołanie zawsze następuje z poziomu okna głównego.

Jak opisano w rozdziale Kontenery najwyższego poziomu, dialog staje się widoczny dopiero po wywołaniu setVisible(true). Przed wywołaniem setVisible(true) musi jednak zostać zakończona konstrukcja oraz inicjalizacja dialogu i jego elementów podrzędnych. Wywołanie setVisible(true) stanowi zatem zawsze ostatnią instrukcję podczas tworzenia dialogu.

W przypadku dialogów modalnych należy uwzględnić, że inaczej niż np. w AWT, wywołanie setVisible(true) nie blokuje działania (nie czeka) do momentu zamknięcia dialogu, lecz natychmiast zwraca sterowanie.

Dla wszystkich klas pochodnych od com.cisag.pgm.gui.Window dostęp do hierarchii kontenerów jest dozwolony wyłącznie poprzez getContentPane() oraz setContentPane() (więcej informacji znajduje się w rozdziale Window).

Rozmiar dialogu (szerokość i wysokość) jest automatycznie obliczany na podstawie wartości preferredWidth oraz preferredHeight panelu zawartości (content pane). W niektórych przypadkach może być konieczne jawne określenie przynajmniej żądanej szerokości dialogu.

Ma to miejsce np. wtedy, gdy użyte elementy lub menedżery układu nie dostarczają sensownych wartości dla preferredWidth lub preferredHeight (więcej informacji znajduje się w rozdziale StandardLayout).

Szerokość i wysokość mogą zostać określone za pomocą metod setWidth() oraz setHeight(). W takim przypadku definiowany jest nie rozmiar panelu zawartości, lecz całego dialogu (łącznie z ramką i paskiem tytułu).

W obu metodach wartości podawane są w postaci szerokości znaków, dzięki czemu ustawienia te są niezależne od rozmiaru czcionki oraz rozdzielczości ekranu.

W rozdziale Używanie dialogów przedstawiono sposób pracy z dialogami na podstawie przykładu kodu.

Dialog API

Poniższe tabele przedstawiają najważniejsze oraz najczęściej używane metody klasy Dialog (pełne oraz szczegółowe opisy znajdują się w dokumentacji JavaDoc klasy Dialog).

Tabela 25: Konstruktory

Metoda Zastosowanie
Dialog(int, Window)
Dialog(int, Window, String)
Tworzy dialog modalny lub niemodalny (pierwszy parametr: Dialog.MODAL lub Dialog.NONMODAL).
Jako drugi parametr (Window) należy podać okno, do którego nowy dialog ma być przypisany. Opcjonalnie jako trzeci parametr można przekazać referencję do obiektu deweloperskiego (DataDescription lub Table) w postaci (bezwzględnej) ścieżki. Na tej podstawie ustalany jest tytuł dialogu. Jeśli nie zostanie podana ścieżka, dialog nie otrzymuje tytułu. Tytuł może jednak zostać ustawiony później za pomocą metod setTitle() (również po wywołaniu setVisible(true)).
Dialog(int, VisualElement)
Dialog(int, VisualElement, String)
Ta grupa konstruktorów zachowuje się analogicznie do powyższych wariantów z parametrem Window jako drugim argumentem.
Przypisane okno jest jednak ustalane automatycznie (za pomocą getWindow()) na podstawie przekazanego elementu VisualElement. Element ten, określany jako „owner”, jest elementem, z którego poziomu dialog ma zostać otwarty. Obszar może wykorzystać tę informację do umieszczenia dialogu na ekranie względnie względem tego elementu.

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.

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.

Jak opisano już w rozdziale Kontenery najwyższego poziomu, okno typu popup (Window) staje się widoczne dopiero po wywołaniu setVisible(true).

Przed wywołaniem setVisible(true) musi zostać zakończona konstrukcja oraz inicjalizacja okna popup i jego elementów podrzędnych. Wywołanie setVisible(true) stanowi zawsze ostatnią instrukcję podczas tworzenia okna popup.

Podobnie jak w przypadku wszystkich klas pochodnych od com.cisag.pgm.gui.Window, dostęp do hierarchii kontenerów jest dozwolony wyłącznie poprzez getContentPane() oraz setContentPane() (więcej informacji na ten temat można znaleźć w rozdziale Window).

Rozmiar okna popup (szerokość i wysokość) jest automatycznie obliczany na podstawie wartości preferredWidth oraz preferredHeight panelu zawartości (content pane).

Dostępne są wprawdzie, podobnie jak w dialogach, metody setWidth() oraz setHeight(), jednak ich implementacje wywołują jedynie setPreferredWidth() oraz setPreferredHeight().

Ponieważ takie rozwiązanie nie jest niezależne od rozmiaru czcionki ani rozdzielczości ekranu, metody te zostały oznaczone jako deprecated i nie powinny być stosowane.

Jeżeli określenie stałego rozmiaru jest bezwzględnie konieczne, należy wywołać setPreferredWidth() lub setPreferredHeight() bezpośrednio na panelu zawartości.

Poniższe tabele przedstawiają najważniejsze oraz najczęściej używane metody klasy PopupWindow (pełne oraz szczegółowe opisy znajdują się w dokumentacji JavaDoc klasy PopupWindow).

Tabela 30: Konstruktory

Metoda Zastosowanie
PopupWindow(VisualElement) Tworzy okno typu popup.
Jako parametr (VisualElement) należy podać element, z którego poziomu okno popup ma zostać otwarte. Element ten służy obszarowi UI z jednej strony do ustalenia okna nadrzędnego, a z drugiej do pozycjonowania okna popup.

Tabela 31: Wygląd wizualny

Metoda Zastosowanie
setShadowVisible(int) Określa, czy okno popup ma być wyświetlane z cieniem oraz jak szeroki ma być cień (w pikselach).
Wartość domyślna jest ustalana przez obszar UI i zasadniczo nie powinna być zmieniana.

Tabela 32: Stan ogólny

Metoda Zastosowanie
isVisible()
setVisible(boolean)
Służy do sprawdzania i zmiany widoczności.
Okno popup staje się widoczne dopiero po jawnym wywołaniu setVisible(true). Za pomocą setVisible(false) może zostać zamknięte.
setAutoHide(boolean) Określa, czy okno popup ma być automatycznie zamykane, gdy użytkownik kliknie poza jego obszarem.
Domyślnie ustawione jest true. Zmiana poprzez setAutoHide() działa tylko wtedy, gdy wywołanie nastąpi przed setVisible(true).

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

Klasa com.cisag.pgm.gui.TabbedPane jest kontenerem wywodzącym się z View (więcej informacji można znaleźć w rozdziale View), który w danym momencie wyświetla tylko jeden ze swoich podelementów. Użytkownik steruje tym elementem za pomocą zakładek (Tabs), przy czym każdemu podelementowi przypisana jest osobna karta. W wyniku wyboru karty widoczny staje się przypisany do niej element.

Dodawanie elementów (zakładek) do TabbedPane musi odbywać się za pomocą specjalnych metod (addTab(…), insertTab(…)). Użycie metod add(…) jest niedozwolone i prowadzi do niezdefiniowanego zachowania.

W przypadku addTab() oraz insertTab() oprócz elementu należy przekazać również Action. Ta Action (z reguły tworzona na podstawie metadanych z repozytorium) definiuje z jednej strony tekst, ToolTip oraz/lub ikonę zakładki, a z drugiej strony służy do identyfikacji i sterowania utworzoną w ten sposób zakładką.

Za pomocą tej Action można np. wybrać zakładkę, ustawić ją jako nieaktywną (disabled) lub ponownie usunąć z TabbedPane. Ponadto istnieje możliwość zarejestrowania się przy tej Action jako ActionListener i otrzymywania powiadomień o otwarciu powiązanej zakładki.

Metoda clear() usuwa wszystkie elementy (regiony). Powinna być stosowana zamiast remove() lub removeAll().

TabbedPane wykorzystuje wewnętrznie własny LayoutManager, dlatego ustawienie innego menedżera układu nie jest możliwe i jest niedozwolone.

TabbedPane API

Poniższe tabele przedstawiają najważniejsze oraz najczęściej używane metody klasy TabbedPane (pełna lista oraz szczegółowe opisy znajdują się w dokumentacji JavaDoc klasy TabbedPane).

Tabela 49: Konstruktory

Konstruktor Zastosowanie
TabbedPane(String) Tworzy puste TabbedPane z zakładkami umieszczonymi u góry (TOP).
Parametr (GUID) służy do identyfikacji TabbedPane (więcej informacji można znaleźć w rozdziale Właściwości ogólne).

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).

  • Przy SMALL_TABS rozmiar czcionki jest zmniejszony.
  • Przy EQUALS_TABS wszystkie zakładki mają jednakową szerokość.
  • Przy EXPANDED_TABS szerokość zakładek jest rozdzielana na całą szerokość TabbedPane (w przeciwnym razie szerokość jest dynamiczna).

Wartości te można ze sobą łączyć (operatorem OR). Jeśli nie zostanie ustawiona żadna wartość, obowiązuje DEFAULT_TABS.
Użycie SMALL_TABS, EQUALS_TABS i/lub EXPANDED_TABS jest niedozwolone, jeżeli pozycja zakładek ustawiona jest na TOP lub BOTTOM.

NO_SCROLL_TAB_LAYOUT
SCROLL_TAB_LAYOUT
AUTO_SCROLL_TAB_LAYOUT
getTabLayoutPolicy()
setTabLayoutPolicy(int)
Określa zachowanie obszaru zakładek w przypadku braku miejsca.

  • Przy NO_SCROLL_TAB_LAYOUT wszystkie zakładki są zawsze wyświetlane; w razie potrzeby ich oznaczenia (np. tekst zakładki) są skracane (…).
  • Przy SCROLL_TAB_LAYOUT teksty zakładek są zawsze wyświetlane; w razie potrzeby liczba wyświetlanych zakładek jest ograniczana (za pomocą dodatkowych elementów przewijania; wszystkie zakładki są dostępne przez menu rozwijane).
  • Przy AUTO_SCROLL_TAB_LAYOUT TabbedPane automatycznie przełącza się z NO_SCROLL_TAB_LAYOUT na SCROLL_TAB_LAYOUT, jeżeli zabraknie 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).

Wskazówka
Działanie LayeredPane nie jest porównywalne z klasą JLayeredPane ze środowiska Swing (tam możliwe jest jednoczesne wyświetlanie wielu elementów, które mogą na siebie dowolnie nachodzić).

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.

Wskazówka
Pola, tj. podklasy com.cisag.pgm.gui.Field (więcej informacji można znaleźć w rozdziale Pola), posiadają zintegrowaną etykietę i dlatego nie wymagają dodatkowego obiektu Label.

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).

Wskazówka
Dostęp do większości metod jest możliwy dopiero po dodaniu etykiety do kontenera.

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ą:

  • bez prowadzenia linii (Label.NONE)
  • linia przy dolnej krawędzi (Label.HORIZONTAL)
  • linia przy lewej krawędzi (Label.VERTICAL)

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 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.

Wskazówka
W trybie wyboru właściwość required (pole obowiązkowe) nie odnosi się bezpośrednio do ValueSetField, lecz do danych, w których wykonywane jest wyszukiwanie. Na podstawie tej właściwości ValueSetField określa, jakie wartości mogą występować w danych:

  • przy required == true mogą występować wyłącznie wartości zdefiniowane w ValueSet,
  • przy required == false może dodatkowo występować 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

BooleanField posiada dwie formy prezentacji, zależne od właściwości selectionMode.

W trybie standardowym pole wykorzystuje typowe pole wyboru (CheckBox).

Oprócz stanów zaznaczony i niezaznaczony za pomocą metody setTriStateSupported(boolean triStateSupported) można opcjonalnie aktywować stan niezdefiniowany.

Dla tego stanu można zdefiniować specjalną ikonę przy użyciu metody setUndefinedIcon(Icon icon).

Wartość BooleanField można ustawić za pomocą metod:

  • setValue(boolean)
  • setValueFromObject(Object)
  • setState(int)

Wartość true odpowiada BooleanField.SELECTED, natomiast false odpowiada BooleanField.DESELECTED.

Wartość BooleanField.UNDEFINED może zostać ustawiona wyłącznie przy użyciu metod setValueFromObject(Object) lub setState(int).

Odpowiednie metody typu get() zwracają aktualny stan w analogicznej formie.

Tryb wyboru

W trybie wyboru BooleanField jest prezentowane w formie listy rozwijanej (ComboBox).

To, jakie stany może przyjmować pole w trybie wyboru, zależy od właściwości required. Właściwość ta standardowo ma wartość false, co oznacza, że możliwe są trzy wartości: tak, nie oraz ignoruj.

Wywołanie metody setRequired(true) powoduje ustawienie pola jako obowiązkowego. W takim przypadku dopuszczalne są wyłącznie wartości tak i nie.

Metody setSelectionString() oraz getSelectionString() mogą być również używane w trybie wyboru do ustawiania lub odczytu stanu pola.

Wartość true odpowiada tak, wartość false odpowiada nie, natomiast wartość null (lub pusty ciąg znaków) jest interpretowana jako ignoruj.

Metody setValue() oraz getValue() działają analogicznie jak w trybie standardowym.

RadioButtonGroup

Klasa com.cisag.pgm.gui.RadioButtonGroup służy – podobnie jak ValueSetField (więcej informacji można znaleźć w rozdziale ValueSetField) – do wprowadzania i wyświetlania danych opartych na typie danych ValueSet.

W przypadku RadioButtonGroup prezentacja oraz wybór wartości odbywają się za pomocą przycisków opcji (RadioButton, więcej informacji można znaleźć w rozdziale RadioButton). Dla każdej wartości z ValueSet tworzony jest oddzielny RadioButton.

Podobnie jak w ValueSetField, w danym momencie może być wybrana tylko jedna wartość.

Aby RadioButtonGroup mogła być traktowana jak każde inne pole, klasa ta dziedziczy po com.cisag.pgm.gui.Field.

Instancję tworzy się przy użyciu konstruktora: RadioButtonGroup(String guid, String path, Action changed, boolean horizontal).

Parametrami są identyfikator GUID, ścieżka oraz opcjonalnie obiekt Action. Parametr horizontal określa, czy przyciski mają być rozmieszczone poziomo (true), czy pionowo (false).

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

Wartość pola można odczytać przy użyciu metod getValue() lub getValueAsObject(), a zmienić za pomocą setValue(CisDecimal) lub setValueFromObject(Object).

PercentField

Klasa com.cisag.pgm.gui.PercentField umożliwia wprowadzanie i wyprowadzanie wartości liczbowych z jednostką procent. Edytowalna jest wyłącznie liczba dziesiętna, natomiast znak procentu pozostaje niezmiennie wyświetlany po prawej stronie liczby.

Pole należy utworzyć standardowo za pomocą konstruktora, przekazując GUID oraz ścieżkę.

Za pomocą metod setValue(CisDecimal) oraz setValueFromObject(Object) można przypisać polu nową
liczbę dziesiętną. Jednostka procent pozostaje przy tym niezmieniona.

Metody getValue() oraz getValueAsObject() zwracają liczbę dziesiętną. Metoda getStringValue() zwraca łańcuch znaków składający się z liczby z dołączonym znakiem procentu.

ByteField, ShortField, IntegerField, LongField

Do wprowadzania i wyprowadzania liczb całkowitych dla każdego typu prymitywnego (byte, short, int, long) dostępna jest osobna klasa dziedzicząca po com.cisag.pgm.gui.Field.

Instancjonowanie pól odbywa się za pomocą ich odpowiedniego konstruktora, któremu należy przekazać GUID oraz ścieżkę.

Metody setValue() oraz getValue() operują na typie danych, który charakteryzuje dane pole.

Funkcji setValueFromObject(Object value) należy przekazać jako wartość odpowiedni dla danego typu danych obiekt java.lang.Object (java.lang.Byte, java.lang.Short, java.lang.Integer, java.lang.Long).

Wartość pola jako java.lang.Object zwracana jest przez metodę getValueAsObject().

Pola umożliwiają wprowadzanie wyłącznie znaków numerycznych, separatorów oraz znaku minus. Wprowadzone dane są analizowane po utracie fokusu i sprawdzane pod kątem zakresu wartości
dopuszczalnego dla danego typu liczbowego.

W przypadku przekroczenia zakresu pole otrzymuje czerwony narożnik, którego kliknięcie powoduje wyświetlenie komunikatu o błędzie (więcej informacji można znaleźć w rozdziale Komunikaty).

Pola uwzględniają ustawienia użytkownika dotyczące wybranych symboli znaku minus oraz separatora grup tysięcy. W ustawieniach użytkownika można również zdefiniować, czy liczby ujemne mają być wyświetlane w kolorze czerwonym.

Maksymalna liczba cyfr jest pobierana z DataDescription. Jeśli nie podano żadnej wartości, obowiązują następujące ustawienia domyślne:

Typ Domyślna maksymalna liczba cyfr całkowitych Domyślna wartość minimalna/maksymalna
BYTE 3 Byte.MIN_VALUE
Byte.MAX_VALUE
SHORT 5 Short.MIN_VALUE
Short.MAX_VALUE
INT 10 Integer.MIN_VALUE
Integer.MAX_VALUE
LONG 19 Long.MIN_VALUE
Long.MAX_VALUE

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.

Wskazówka
Opisana tutaj klasa udostępnia jedynie funkcjonalność bazową. W typowych aplikacjach Comarch ERP Enterprise należy stosować klasę PointInTimeField z pakietu com.cisag.app.general.gui.

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().

Wskazówka
W przeciwieństwie do klasy com.cisag.pgm.gui.Label, LabelField może wyświetlać tekst albo ikonę, lecz nie oba elementy jednocześnie. Możliwość wyświetlania ikon za pomocą LabelField nie powinna być jednak wykorzystywana. Zamiast tego należy używać IconField (więcej informacji można znaleźć w rozdziale IconField).

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.

Wskazówka
W przypadku list i tabel aktualizacja klienta musi zostać wywołana jawnie za pomocą metody display(). Zasada ogólna brzmi: po każdym dostępie do listy lub tabeli należy na zakończenie wywołać display(), aby model był spójny i wszystkie zmiany zostały odzwierciedlone po stronie klienta.

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().

Wskazówka
W listach przy selekcji wielokrotnej często stosuje się inne rozwiązanie — w każdej linii umieszczany jest dodatkowy CheckBox.

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

Wyświetlanie rekordów odbywa się strona po stronie, tzn. spośród n (załadowanych) rekordów na kliencie prezentowanych jest każdorazowo tylko m.

Poprzez przewijanie stron (więcej informacji można znaleźć w rozdziale PageButton) wyświetlane są następne lub poprzednie m rekordów.

Wartość m określana jest jako Page-Size i może zostać zdefiniowana w konstruktorze list i tabel lub za pomocą metody setPageSize(int value). Optymalnie wartość ta powinna być dobrana tak, aby lista lub tabela w pełni wykorzystywała dostępny obszar wyświetlania, lecz jeszcze bez konieczności użycia paska przewijania.

Zaleca się, o ile to możliwe, całkowite wyłączenie przewijania pionowego za pomocą metody
setVerticalScrollBarPolicy(VERTICAL_SCROLLBAR_NEVER). W takim przypadku optymalna Page-Size jest automatycznie obliczana i aktualizowana przez listę/tabelę. Wartość przekazana w konstruktorze ma wtedy znaczenie drugorzędne, jednak powinna wynosić co najmniej 1.

Dodatkowo listy i tabele udostępniają następujące metody:

  • int getCurrentVisibleRowCount();
  • setCurrentVisibleRowCountChangeAction(Action a);

Pierwsza metoda zwraca aktualną liczbę widocznych wierszy, którą następnie można ustawić za pomocą setPageSize().

Druga metoda umożliwia zarejestrowanie akcji (Action), która jest wywoływana zawsze wtedy, gdy zmieni się wartość currentVisibleRowCount. Za pomocą tych dwóch metod można również samodzielnie zaimplementować automatyczne dopasowanie Page-Size. Obowiązuje jednak to samo ograniczenie — wysokość wszystkich wierszy musi być stała.

Eksport danych

Zarówno lista, jak i tabela posiadają ogólną implementację eksportu danych. Wszystkie już załadowane rekordy są eksportowane jako plik CSV.

Eksport danych opiera się bezpośrednio na elementach wizualnych (VisualElements) używanych w wierszu. Każdy VisualElement określa poprzez wartość zwracaną przez metodę getDataColumnSupport(), w jaki sposób ma zostać wyeksportowany:

  • jeżeli metoda zwraca null, element jest ignorowany (chyba że jest kontenerem — wówczas jest przeszukiwany rekurencyjnie),
  • poprzez Column-ID element wizualny jest przypisywany do kolumny,
  • element wizualny może podzielić swoje dane na kilka podkolumn,
  • dla każdej podkolumny można odczytać wartość oraz nagłówek.

Wszystkie klasy pochodne od com.cisag.pgm.gui.Field posiadają odpowiednią implementację standardową. Zwraca ona GUID pola jako Column-ID. Pozostałe właściwości zależą od konkretnego typu pola. Nagłówek (również dla podkolumn) zawsze bazuje na Label z DataDescription.

Podczas tworzenia list lub tabel należy zatem zadbać o to, aby wszystkie używane pola posiadały jednoznaczny GUID oraz sensowną DataDescription. W przeciwnym razie pola te mogą nie zostać uwzględnione w eksporcie danych lub zostać wyeksportowane nieprawidłowo.

List

Stosowanie list wymaga wykonania następujących kroków:

  • utworzenie instancji List,
  • implementacja ListViewCreator (więcej informacji można znaleźć w rozdziale ListViewCreator),
  • implementacja jednego lub kilku ListView (więcej informacji można znaleźć w rozdziale ListView),
  • udostępnienie jednego lub kilku widoków dla nagłówków kolumn,
  • zdefiniowanie wspólnych kolumn dla ListView,
  • (opcjonalnie) udostępnienie widoku dla stopki,
  • (opcjonalnie) wykorzystanie MetaCheckBox do selekcji wielokrotnej.

Tworzenie list

Utworzenie listy odbywa się za pomocą konstruktora List(String guid, int size, boolean editable, ListViewCreator creator).

Parametr guid przypisuje liście (VisualElement) identyfikator GUID. Parametr size określa (początkową) wielkość strony listy. Parametr editable definiuje, czy lista jest listą edytowalną, czy tzw. listą wyjściową. Parametr creator przekazuje liście fabrykę do tworzenia ListView.

W razie potrzeby ListViewCreator może zostać ustawiony później za pomocą metody setListViewCreator(ListViewCreator).

Nagłówki kolumn

Ponieważ wizualizacja rekordów odbywa się przez ListView, lista nie może samodzielnie udostępniać odpowiadających im nagłówków kolumn. Udostępnia jednak miejsce oraz odpowiednie metody umożliwiające wstawienie własnych widoków jako nagłówków kolumn w obszarze nagłówka.

Struktura używanych ListView powinna zostać odwzorowana przez jeden lub kilka wierszy (widoków).

Do dodawania tych widoków służą metody:

  • addTitleBar(View view)
  • addTitleBar(View view, int styleLevel)

Parametr styleLevel może zostać wykorzystany do nadania nagłówkom specjalnych atrybutów czcionki.

Wskazówka
Widoki używane jako nagłówki powinny — podobnie jak ListView — wyrównywać swoje elementy względem linii prowadzących listy, tzn. jako menedżera układu należy zastosować StandardLayout (pusty konstruktor).

Jeżeli do listy za pomocą addTitleBar(View view) dodano jeden lub kilka nagłówków, na pasku narzędzi listy automatycznie pojawi się dodatkowy element sterujący. Za pomocą tego ToggleButton użytkownik może włączać i wyłączać widoczność nagłówków kolumn.

Szerokości kolumn

Aby zawartość wierszy (ListView), nagłówków oraz stopki była wyrównana względem wspólnych linii prowadzących, linie prowadzące (kolumny) powinny być definiowane wyłącznie na poziomie listy (więcej informacji można znaleźć w rozdziale StandardLayout).

W tym celu lista udostępnia metody:

  • setColumnWidths(double[] widths)
  • setColumnWidths(double[] widths, double[] leftMargins, double[] rightMargins)

Specyfikacja szerokości oraz odstępów odbywa się analogicznie do metod setGuides() klasy VisualElementContainer, przy czym szerokość etykiety (Label) jest tutaj zawsze ustawiana na 0.

Wskazówka
Aby zdefiniowane linie prowadzące listy były rzeczywiście wykorzystywane do wyrównania nagłówków, ListView oraz stopki, w tych elementach nie mogą być definiowane własne linie prowadzące (kolumny).

Obszar stopki

Analogicznie jak w przypadku nagłówków kolumn, lista udostępnia miejsce oraz metody do stosowania wierszy stopki lub sum.

Struktura używanych ListView może zostać odwzorowana przez jeden lub kilka wierszy (widoków). Do dodawania tych widoków służy metoda addTotalBar(View view).

Wiersze stopki są zawsze wyświetlane na dolnej krawędzi listy (nie jako ostatni rekord danych).

MetaCheckBox

MetaCheckBox jest przeznaczona do zastosowań, w których wybór rekordów odbywa się za pomocą CheckBoxów (w ListView), a funkcja Zaznacz wszystkie ma być realizowana przez dodatkową CheckBox na pasku narzędzi listy.

MetaCheckBox dla paska narzędzi można utworzyć za pomocą metody *createMetaCheckBox(int column, String view)*.

Parametr column określa pozycję (kolumnę) w ListView, parametr view nie pełni funkcji i powinien mieć wartość null.

CheckBoxy dla poszczególnych ListView muszą zostać utworzone przy użyciu metody createDependentCheckBox(String view) i ręcznie dodane do danego ListView. Parametr view musi odpowiadać nazwie ListView.

ListStyle

Klasa com.cisag.pgm.gui.ListStyle definiuje różne właściwości wizualizacji dla list oraz ListView. Do tych właściwości należą m.in.:

  • kolor tła,
  • kolor czcionki,
  • krój, styl i rozmiar czcionki,
  • rodzaj obramowania,
  • sposób prezentacji zaznaczonych wpisów.

Obecnie zdefiniowane są następujące wartości:

ListStyle Zastosowanie
DIALOG Listy dialogowe
OUTPUT Listy wyjściowe
SEARCH Listy wyników wyszukiwania
XOUTPUT Rozszerzone listy wyjściowe (ExtendedListView)
XINPUT Rozszerzone listy dialogowe (ExtendedListView)
XINPUT2 Rozszerzone listy dialogowe (ExtendedListView) — drugi poziom
XINPUT3 Rozszerzone listy dialogowe (ExtendedListView) — trzeci poziom

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)

  1. Parametr guid przypisuje tabeli (VisualElement) identyfikator GUID.
  2. Parametr size określa (początkowy²) rozmiar strony tabeli.
  3. Parametr editable określa, czy tabela jest tabelą edytowalną, czy wyłącznie tabelą wyjściową³.
  4. 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);

void dataFromTable(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].

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.

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)).

Uwaga
W aktualnej wersji klasa Tree dziedziczy po VisualElementContainer (zamiast po VisualElement). Właściwości i metody odziedziczone z VisualElementContainer nie są jednak obsługiwane przez klasę Tree i nie mogą być wykorzystywane.

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().

Uwaga
Dodawanie lub usuwanie węzłów powinno odbywać się zawsze za pomocą odpowiednich metod klasy Tree. Klasa TreeNode również udostępnia takie metody, jednak ich użycie nie powoduje automatycznej aktualizacji widoku drzewa. W takim przypadku, aby odświeżyć widok, należałoby np. ponownie przypisać węzeł główny (root) do obiektu Tree.

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.

Uwaga
EditableTreeView oraz TreeView dla danego obiektu TreeNode powinny być ze sobą spójne, tzn. struktura, rozmiar i pozycja elementów podrzędnych powinny być identyczne – z wyjątkiem elementu wykorzystywanego do edycji.

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.

Wskazówka
W przypadku komponentów EntityField menu kontekstowe jest udostępniane automatycznie, tzn. przez system (więcej informacji można znaleźć w rozdziale EntityField), dlatego jawne tworzenie lub konfigurowanie menu popup nie jest konieczne.

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.

Uwaga
Przy stosowaniu StandardLayout należy pamiętać, że nie wszystkie konstrukcje kontenerów są związane z definicją linii prowadzących.

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:

container.add(field, „1”);

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:

container.add(field, „0-1”);

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 wColumn = 1.0 / 2.0; // two columns
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):

container.add(
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.

Wskazówka
Znaczenie właściwości alignmentY (lub alignmentX) jest często błędnie interpretowane. Nie określa ona sposobu wyrównania elementów względem kontenera, lecz sposób wyrównania elementów względem siebie. Właściwość alignmentY definiuje względny punkt odniesienia między górną (0.0) a dolną (1.0) krawędzią elementu. Analogicznie właściwość alignmentX definiuje względny punkt odniesienia między lewą (0.0) a prawą (1.0) krawędzią elementu. Wyrównanie odbywa się poprzez takie przesunięcie elementów względem siebie, aby ich punkty odniesienia znalazły się na jednej linii.

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.

Do tworzenia instancji DecoratedBorder dostępne są — oprócz odpowiednich konstruktorów — również metody statyczne (createBorder()*).

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.

Wskazówka
Podobnie jak w przypadku kolorów, framework UI wykorzystuje do definiowania czcionek ustawienia Look & Feel zależne od kroju pisma. Także w tym przypadku ustawienia te nie powinny być w miarę możliwości nadpisywane przez bezpośrednie (niezależne od użytkownika) wartości.

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.

Wskazówka
W listach i drzewach wymagany jest zredukowany styl czcionki (więcej informacji można znaleźć w Comarch ERP Enterprise Styleguide). W tym celu zamiast getFont(Font.PLAIN, 7) należy stosować UIManager.getFont(„ListStyle.font_7pt”). W tym przypadku wartości są pobierane z UIManager, jednak nie poprzez klucz absolutny, lecz poprzez klucz względny.

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

Klasa com.cisag.pgm.gui.UIManager zarządza wszystkimi właściwościami zależnymi od aktualnego Look & Feel, które są wykorzystywane przy tworzeniu elementów interfejsu użytkownika. Obejmują one w szczególności ustawienia dotyczące kolorów, czcionek, ikon, ramek oraz odstępów. Dzięki centralnemu zarządzaniu zapewniony jest jednolity wygląd powierzchni, a zmiany (Face-lifting) mogą być przeprowadzane centralnie.

UIManager jest w dużej mierze wykorzystywany przez same elementy, które pobierają z niego swoje wartości (parametry), np. jaki kolor ustawiany jest dla paska tytułu w przypadku UIManager, jakie ramki stosowane są dla przycisków lub jak duże odstępy krawędziowe ustawione są w Label albo jaki kolor ma zaznaczony element w tabeli.

Ustawienia pobierane za pomocą UIManager nie powinny być zmieniane. W przeciwnym razie mogłoby dojść zarówno do automatycznego dopasowania do ustawień użytkownika lub klienta, jak i do utraty spójności interfejsu użytkownika.

Jeżeli jednak zmiana ustawień wstępnych jest konieczna, należy stosować wartości względne (odwołania do UIManager), zamiast ustalać stałe wartości (np. wartości RGB, piksele).

UIManager udostępnia również aktywną tabelę (Map), w której właściwości zależne od Look & Feel są zapisywane jako pary klucz-wartość (Key/Value). Do zapytań dostępne są następujące metody:

  • public Border getBorder(Object key)
  • public Color getColor(Object key)
  • public Font getFont(Object key)
  • public Icon getIcon(Object key)
  • public Insets getInsets(Object key)
  • public Insets getInsets(Object key,VisualElement elem)
  • public int getInt(Object key)
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ć „zwi­nię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ą.

Wskazówka
W aplikacjach Comarch ERP Enterprise należy stosować klasę com.cisag.pgm.gui.ButtonBar (więcej informacji można znaleźć w rozdziale ButtonBar), jeśli w oknie dialogowym ma być wyświetlany jeden lub kilka przycisków. Klasa ta nie tylko zmniejsza nakład pracy, ale również zapewnia stosowanie jednolitego sposobu rozmieszczania elementów układu we wszystkich oknach dialogowych.

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

MenuButtons (więcej informacji można znaleźć w rozdziale MenuButton) są zazwyczaj stosowane w listach symboli (więcej informacji można znaleźć w rozdziale CoolBar).

Przykład tworzy najpierw instancję ActionList i dodaje do niej odpowiednie (podrzędne) Actions. Obiekt ActionList jest podklasą Action i zawiera ustawienie Actions, które jako ścieżkę podają metadane. Na podstawie MenuButton określane są z metadanych: ikona (lub tekst), skrót oraz ToolTip, a także bezpośrednio dla przycisku głównego. Z dodanych Actions generowane są wpisy menu MenuButton.

Wskazówka
W przykładzie MenuButton jest tworzony jawnie przy użyciu new i dodawany za pomocą add(VisualElement) do MainCoolBar. W przypadku MainCoolBar nie jest to konieczne, ponieważ MainCoolBar jest konfigurowany za pomocą metadanych (ActionList). Jeśli metoda add(Action) zostanie wywołana z obiektem ActionList, MenuButton zostanie utworzony pośrednio. W przypadku innych list symboli (gui.CoolBar) taki sposób nie jest jednak możliwy i dlatego został on zastosowany w tym przykładzie.

…
public class ExampleApplication extends CisUiApplication {

…
// identyfikatory Action
private static final int CONFIRM = 50;
private static final int PICK = 51;
private static final int DELIVER = 52;
private static final int INVOICE = 53;
private static final int CASH_INVOICE = 54;
private static final int PROFORMA_INVOICE = 55;
private static final int ONHAND_DISTRIBUTION = 56;
private static final int RELEASE = 60;
private static final int HOLD = 61;

…
// instancje Action
private ActionList executeList;
private Action confirm;
private Action proformaInvoice;
private Action onhandDistribution;
private Action pick;
private Action deliver;
private Action invoice;
private Action cashInvoice;
private Action release;
private Action hold;

…

protected void initCoolBar() {
MainCoolBar cb = getMainCoolBar();

…
// utworzenie ActionList
executeList = new ActionList(
0,
„com.cisag.app.sales.order.ui.OrderMaintenanceExecute”
);

// utworzenie Actions (wpisy menu)
confirm = new Action(
CONFIRM,
„com.cisag.app.sales.order.ui.OrderMaintenanceGenerateConfirm”,
this
);

proformaInvoice = new Action(
PROFORMA_INVOICE,
„com.cisag.app.sales.order.ui.OrderMaintenanceGenerateProformaInvoice”,
this
);

onhandDistribution = new Action(ONHAND_DISTRIBUTION,
„com.cisag.app.sales.order.ui.OrderMaintenanceGenerateOnhandDistribution”, this);
pick = new Action(PICK,
„com.cisag.app.sales.order.ui.OrderMaintenanceGeneratePickingOrder”, this);
deliver = new Action(DELIVER,
„com.cisag.app.sales.order.ui.OrderMaintenanceGenerateShippingOrder”, this);
invoice = new Action(INVOICE,
„com.cisag.app.sales.order.ui.OrderMaintenanceGenerateInvoice”, this);
cashInvoice = new Action(CASH_INVOICE,
„com.cisag.app.sales.order.ui.OrderMaintenanceGenerateCashInvoice”, this);
release = new Action(RELEASE, „com.cisag.app.general.order.ui.ReleaseOrder”, this);
hold = new Action(HOLD, „com.cisag.app.general.order.ui.HoldOrder”, this);

// dodanie Actions do ActionList
executeList.add(release);
executeList.add(confirm);
executeList.add(proformaInvoice);
executeList.add(onhandDistribution);
executeList.add(pick);
executeList.add(deliver);
executeList.add(invoice);
executeList.add(cashInvoice);
executeList.add(hold);

// ostatniego wyboru nie zapisywać (standard dla MenuButtons)
executeList.setMemoryActive(false);

// utworzenie MenuButton dla MainCoolBar (z dużymi ikonami)
MenuButton mb = new MenuButton(Guid.AUTOGUID, executeList, true);

// dodanie MenuButton do (Main)CoolBar
cb.add(mb);
…
}
…

public void performAction(Action action) {
…
}

int actionId = action.getId();
switch (actionId) {
case CONFIRM:
/** @todo implementacja/wywołanie „confirm” */
break;

case PROFORMA_INVOICE:
/** @todo implementacja/wywołanie „proforma_invoice” */
break;

…
}
…
}
…
}

Wykorzystanie okien dialogowych

W przykładzie wykorzystano modalne okno dialogowe do edycji pola danych. Do edycji tekstu, np. w celu otwarcia okna dialogowego edycji, używany jest tzw. przycisk Dialog (patrz Dialog-Button). Otwarcie takiego okna dialogowego może zostać wywołane również przez inną akcję użytkownika (Action).

W przykładzie chodzi o prosty (wielowierszowy) tekst, który sam w sobie nie wymagałby osobnego edytora. W praktyce zaleca się jednak stosowanie oddzielnego okna dialogowego do edycji tylko wtedy, gdy przynosi to użytkownikowi rzeczywistą korzyść.

Wskazówka
Do wyświetlania komunikatów lub okien dialogowych potwierdzenia należy stosować klasę com.cisag.pgm.gui.ConfirmationDialog.

Klasa TextEditor wewnętrznie tworzy instancję com.cisag.pgm.gui.Dialog i wypełnia ją odpowiednimi elementami interfejsu użytkownika. Do potwierdzania lub anulowania edycji wykorzystywana jest instancja com.cisag.pgm.gui.ButtonBar (więcej informacji można znaleźć w rozdziale ButtonBar). Komunikacja z klasą odbywa się według następującego schematu:

  • utworzenie instancji TextEditor. Jako Owner należy wskazać element, do którego należy okno dialogowe lub z którego zostało ono otwarte. Na podstawie tego elementu określane jest z jednej strony nadrzędne okno, a z drugiej strony służy on jako wskazówka do pozycjonowania okna dialogowego.
  • za pomocą setClosedAction() można zarejestrować Action, która zostanie wywołana po zamknięciu okna dialogowego (przez użytkownika lub programowo)
  • za pomocą setValue(String) można ustawić w TextEditor tekst przeznaczony do edycji
  • po zakończeniu konfiguracji okno dialogowe można wyświetlić za pomocą show()
  • po zamknięciu okna dialogowego przez użytkownika przyciskiem [OK], [Anuluj] lub [X] zostaje wywołana (jeśli została ustawiona przez setClosedAction()) tzw. Closed-Action
  • za pomocą isApproved() można sprawdzić, czy okno dialogowe zostało zamknięte przyciskiem [OK], czy anulowane
  • w przypadku [OK] (approved) aktualną wartość można odczytać przy użyciu getValue()
  • instancja TextEditor może być używana wielokrotnie. W takim przypadku dwa pierwsze kroki nie muszą być ponownie wykonywane
Wskazówka
W odróżnieniu np. od java.awt.Dialog, metoda show() (lub setVisible(true)) również przy modalnych oknach dialogowych nie zwraca sterowania do programu dopiero po zamknięciu okna. Program nie oczekuje więc na zamknięcie okna dialogowego. Zamiast tego dostępna jest Closed-Action, która zostaje wywołana po zamknięciu okna dialogowego (niezależnie od sposobu jego zamknięcia).

public class TextEditor {

private static final int DIALOG_CLOSED = 100;

private Dialog dialog;
private boolean approved;

private MultiLineTextField textField;

private Action closedAction;

public TextEditor(VisualElement owner) {
dialog = new Dialog(Dialog.MODAL, owner);
dialog.setTitleFrom(…); // „Demo Dialog”

DialogActionListener listener = new DialogActionListener();
dialog.setClosedAction(new Action(DIALOG_CLOSED, listener));

View contentPane = dialog.getContentPane();
contentPane.setLayout(new BoxLayout(BoxLayout.Y_AXIS));

Label prompt = new Label(Guid.AUTOGUID, …); // „Opis:”
contentPane.add(prompt);
prompt.setAlignmentX(Label.CENTER_ALIGNMENT);

prompt.setMaximumWidth(Short.MAX_VALUE);

contentPane.add(new Strut(Strut.VERTICAL, 5));

textField = new MultiLineTextField(Guid.AUTOGUID, „”);
contentPane.add(textField);
textField.setLabel(null);
textField.setAlignmentX(MultiLineTextField.CENTER_ALIGNMENT);
textField.setMinimumHeight(0);
textField.setMaximumWidth(Short.MAX_VALUE);
textField.setMaximumHeight(Short.MAX_VALUE);

contentPane.add(new Strut(Strut.VERTICAL, 5));

ButtonBar buttonBar = new
ButtonBar(ButtonBar.OK | ButtonBar.CANCEL, listener);
contentPane.add(buttonBar);
buttonBar.setMaximumHeight(0);
}

public String getValue()
{
return textField.getStringValue();
}

public void setValue(String text)
{
textField.setStringValue(text);
}

public void setClosedAction(Action action)
{
closedAction = action;
}

public void show()
{
if (isVisible()) {
throw new java.lang.IllegalStateException(„already shown”);
}
approved = false;
dialog.setVisible(true);
}

public void approve()
{
if (!isVisible()) {
throw new java.lang.IllegalStateException(„dialog not shown”);
}
approved = true;
dialog.setVisible(false);
}

public void cancel()
{
if (!isVisible()) {
throw new java.lang.IllegalStateException(„dialog not shown”);
}
approved = false;
dialog.setVisible(false);
}

public boolean isVisible()
{
return dialog.isVisible();
}

public boolean isApproved()
{
return approved;
}

private class DialogActionListener implements ActionListener
{
public void performAction(Action action)
{
int actionId = action.getId();
System.out.println(„actionId = ” + actionId);
if (actionId == ButtonBar.OK) {
approve();
}
else if (actionId == ButtonBar.CANCEL) {
cancel();
}
else if (actionId == DIALOG_CLOSED) {
if (closedAction != null) {
closedAction.setState(new Boolean(isApproved()));
closedAction.fireAction();
…
}
}
}

Program aplikacji wykorzystuje dwie Actions do komunikacji z TextEditor:

  • pierwsza Action jest rejestrowana przy polu tekstowym i uruchamia tworzenie lub otwarcie okna dialogowego
  • druga Action jest rejestrowana przy TextEditor i uruchamia dalsze przetwarzanie

Instancja TextEditor jest tworzona, gdy użytkownik po raz pierwszy kliknie przycisk Dialog. Ta sama instancja jest następnie wykorzystywana również przy kolejnych wywołaniach.

Wskazówka
Dopóki okno dialogowe nie zostało zamknięte, aplikacja powinna przechowywać (w Javie) referencję do tego okna, aby moduł Garbage Collector nie zniszczył go przedwcześnie.

public class ExampleApplication extends CisUiApplication {

…

private static final int OPEN_DIALOG_ACTION = 200;
private static final int DIALOG_CLOSED_ACTION = 201;

…

private MultiLineTextField description;
private TextEditor editor;

private Action openDialogAction;
private Action dialogClosedAction;

…

protected void createWorkArea() {
…

description = new MultiLineTextField(Guid.AUTOGUID, …); // „Opis:”
pane.add(description, „0”);

openDialogAction = new Action(OPEN_DIALOG_ACTION, this);
description.setLabelPosition(MultiLineTextField.LABEL_LEFT);
description.setEditable(false);
…
description.setDialogAction(openDialogAction);

…
}

…

public void performAction(Action action) {

switch (action.getId()) {
…

case OPEN_DIALOG_ACTION:
if (editor == null) {
editor = new TextEditor(description);
editor.setClosedAction(new Action(DIALOG_CLOSED_ACTION, this));
}

if (!editor.isVisible()) {
editor.setValue(description.getStringValue());
editor.show();
}
break;

case DIALOG_CLOSED_ACTION:
if (editor.isApproved()) {
description.setStringValue(editor.getValue());
}
else {
// pominięto
}
break;

…
}
…
}
…
}

Graficzny debugger

Graficzny debugger służy jako narzędzie analityczne dla interfejsów użytkownika utworzonych przy użyciu frameworka UI. Narzędzie to umożliwia przeglądanie oraz modyfikowanie wszystkich elementów znajdujących się w oknie, a także ich właściwości.

Za pomocą graficznego debuggera można stosunkowo łatwo znaleźć i usunąć typowe błędy interfejsu użytkownika, takie jak np. nieprawidłowe menedżery układu, zagnieżdżone ScrollPanes, niewidoczne elementy czy brakujące opisy danych. Ponieważ większość właściwości może być nie tylko wyświetlana, lecz także bezpośrednio zmieniana, możliwe jest również interaktywne testowanie różnych konfiguracji elementów w celu znalezienia optymalnych wartości.

Graficzny debugger wykorzystuje jako wizualizację okno dialogowe (niemodalne). W tym oknie po lewej stronie wyświetlana jest hierarchia kontenerów analizowanego interfejsu.

Kontenery najwyższego poziomu (okno główne, okno dialogowe, okno popup) są przedstawiane w formie drzewa. Po prawej stronie wyświetlane są właściwości wybranego elementu i mogą być one tam również modyfikowane.

Wskazówka
Wyświetlane elementy nie są klasami z pakietu com.cisag.pgm.gui, lecz klasami z pakietu com.cisag.pgm.dialog, ponieważ implementacje pakietu com.cisag.pgm.gui dziedziczą z tych klas (więcej informacji można znaleźć we wprowadzeniu). Ma to również wpływ na wyświetlaną hierarchię kontenerów oraz pokazywane właściwości elementów. Elementy obu pakietów są jednak zasadniczo bardzo podobne (pakiet com.cisag.pgm.dialog jest jedynie nieco bardziej szczegółowy), dlatego w praktyce nie powoduje to istotnych niedogodności.

Wywołanie

Graficzny debugger może zostać uruchomiony bezpośrednio z działającej aplikacji. Warunkiem jest posiadanie przez użytkownika odpowiednich uprawnień (więcej informacji można znaleźć w rozdziale Uprawnienia).

Wywołanie odbywa się z klawiatury przy użyciu kombinacji klawiszy [Ctrl]+[Alt]+[G].

Wskazówka
Graficzny debugger zawsze generuje elementy z okna, z którego został uruchomiony. Dlatego przed jego uruchomieniem fokus klawiatury musi zostać ustawiony w odpowiednim oknie (lub w jego elemencie podrzędnym).

Uprawnienia

Aby uruchomić graficzny debugger, w obszarze frameworka Rozwój oprogramowania (com.cisag.pgm.SoftwareDevelopment) wymagana jest zdolność uruchamiania graficznego debuggera (com.cisag.sys.kernel.browser.startGraphicDebugger).

Uwaga
W systemie produkcyjnym zdolność ta nie powinna być przypisana żadnemu użytkownikowi. Z jednej strony istnieje ryzyko, że za pomocą graficznego debuggera możliwe byłoby obejście innych ustawień uprawnień. Na przykład dane (pola) mogą zostać uczynione widocznymi, mimo że dla danego użytkownika powinny pozostać ukryte. Z drugiej strony manipulacja właściwościami elementów może prowadzić do poważnych błędów w działających aplikacjach.

Obsługa

Lewa część graficznego debuggera (nagłówek Elements) przedstawia hierarchię kontenerów analizowanych elementów najwyższego poziomu w formie drzewa. Kontenery najwyższego poziomu stanowią korzeń drzewa, a podkontenery i elementy tworzą jego węzły i liście. Drzewo służy jedynie do wizualizacji elementów kontenera, których właściwości mają być wyświetlane po prawej stronie okna graficznego debuggera.

Prawa część okna debuggera (nagłówek Properties) pokazuje właściwości wybranych elementów. Wyświetlane właściwości zależą od odpowiedniej klasy elementu. Pokazywane są wyłącznie te właściwości, które są wspólne dla wszystkich wybranych elementów.

Wyszukiwanie elementów

Aby szybko znaleźć określony element w hierarchii kontenerów, graficzny debugger udostępnia funkcję wyszukiwania.

W polu Search for wpisany (częściowy) tekst jest wyszukiwany w wybranym kierunku (up/down), z uwzględnieniem lub bez rozróżniania wielkości liter (case sensitive), w hierarchii kontenerów. Podczas wyszukiwania brane są pod uwagę następujące właściwości elementów:

  • _id — wszystkie elementy
  • _text (Label) — wszystkie pola
  • _label — wszystkie pola

Jeśli zostanie znaleziony element, w którego właściwości wpisany tekst jest zawarty (pełna zgodność nie jest wymagana), element ten zostaje zaznaczony w drzewie i wyświetlony w prawym polu właściwości. Przy kolejnym wyszukiwaniu wyszukiwanie jest kontynuowane od następnego elementu (w kierunku up lub down).

Wskazówka
Teksty opisów pól (Label) zazwyczaj zawierają również znak sterujący &, który służy do oznaczania Access Keys.

Identyfikatory elementów

Każdy element w pakiecie com.cisag.pgm.dialog ma unikalny identyfikator elementu (ID). Identyfikator ten jest wykorzystywany przede wszystkim do komunikacji między klientem a serwerem. W połączeniu z graficznym debuggerem może być jednak bardzo przydatny do odnalezienia określonego elementu. Na przykład w wyszukiwaniu można wpisać numer ID, aby znaleźć dany element.

Warunkiem wyszukiwania według identyfikatora elementu jest oczywiście znajomość identyfikatora szukanego elementu. Aby ustalić identyfikator elementu, istnieją następujące możliwości:

  • jeśli element ma fokus, identyfikator aktualnego elementu można wyświetlić w małym oknie popup przy użyciu kombinacji klawiszy [Ctrl]+[Shift]+[F]. Okno to wyświetla identyfikator elementu, który aktualnie ma fokus.
  • kliknąć element myszą, przytrzymując jednocześnie klawisz [Ctrl]. W pasku statusu przeglądarki zostanie wyświetlony identyfikator klikniętego elementu. Może się jednak zdarzyć, że informacja ta zostanie nadpisana przez inne komunikaty paska statusu i nie będzie już dostępna do odczytu. W trybie HTML metoda ta nie jest dostępna (brak statusu dla elementów z możliwością ustawienia fokusu), dlatego lepiej zastosować pierwszą z opisanych metod.
  • alternatywnie można wybrać dowolny element znajdujący się w bezpośrednim sąsiedztwie szukanego elementu i w widoku drzewa przejść do właściwego elementu.

Identyfikatory elementów są zawsze zbudowane według wzorca _id, po którym następuje liczba. Jeśli po liczbie pojawiają się dodatkowe znaki, nie jest to identyfikator elementu. W takim przypadku należy powtórzyć jeden z opisanych wcześniej kroków.

Ponieważ graficzny debugger jest realizowany jako niemodalne okno dialogowe, w ten sposób można wyszukiwać kolejne elementy bez konieczności zamykania okna graficznego debuggera.

Wyświetlanie i zmiana właściwości

W zależności od typu (podklasy) wybranych elementów, po prawej stronie wyświetlane są jedna lub kilka grup właściwości. Jeśli wybranych jest kilka elementów, wyświetlane są tylko te grupy właściwości, które obowiązują dla wszystkich wybranych elementów.

Grupowanie właściwości odpowiada podziałowi klas elementów (więcej informacji można znaleźć w rozdziale Podklasy elementów VisualElement – com.cisag.pgm.dialog). Na przykład właściwości klasy VisualElement (com.cisag.pgm.dialog) są zestawione w jednej grupie (ponieważ są wspólne dla wszystkich elementów), natomiast właściwości pola (com.cisag.pgm.dialog) w innej. Istniejące dodatkowe właściwości (zaimplementowane w podklasach) są wizualizowane w osobnych sekcjach.

Obecnie graficzny debugger wyświetla następujące właściwości elementów:

  • com.cisag.pgm.dialog.VisualElement
  • com.cisag.pgm.dialog.VisualElementContainer
  • com.cisag.pgm.dialog.Field
  • com.cisag.pgm.dialog.DecimalField
  • com.cisag.pgm.dialog.QuantityField
  • com.cisag.pgm.dialog.ValueSetField
  • com.cisag.pgm.dialog.Label
  • com.cisag.pgm.dialog.ListView
  • com.cisag.pgm.dialog.Scrollable
  • com.cisag.pgm.dialog.SmartButton

W zależności od właściwości do ich wyświetlania i edycji używane są proste pola tekstowe, pola wyboru (CheckBox), pola listy (ComboBox) lub dodatkowe przyciski czy okna dialogowe.

W przypadku bardziej złożonych właściwości (o charakterze obiektowym) dostępne jest – w razie potrzeby – okno dialogowe lub popup (specyficzny dla Comarch ERP Enterprise przycisk ze znakiem …). Aktualna implementacja ogranicza się jednak do następujących właściwości lub typów obiektów:

  • Border
  • Font
  • Data Description
  • Color (background, foreground, selectionBackground, selectionForeground, controlBackground, controlForeground)
Zmiana właściwości

Właściwości można zmieniać w zależności od typu danych, np. poprzez zaznaczenie (CheckBox) lub wprowadzenie tekstu (TextField). Przejęcie nowej wartości w polach tekstowych następuje jednak dopiero przy zmianie pola (utracie fokusu) lub przy użyciu przycisków [OK] albo [Zastosuj] w oknach dialogowych.

Uwaga
Zmiana właściwości elementów może prowadzić do poważnych błędów w powiązanej aplikacji.

Wskazówka
Nie wszystkie zmiany są odwracalne, tzn. wartość można wprawdzie zmienić, ale nie można przywrócić poprzedniej wartości (domyślnej).

Zachowanie przy wielokrotnym wyborze

Niektóre właściwości, w szczególności wartości logiczne (boolean), obsługują jednoczesną edycję wielu elementów. W przypadku wielokrotnego wyboru wyświetlana jest tylko taka konkretna wartość, która jest identyczna dla wszystkich wybranych elementów. Jeśli wartości są różne, wyświetlana jest wartość nieokreślona. Gdy dla danej właściwości ustawiona zostanie nowa wartość (również z wartości nieokreślonej), zostaje ona zmieniona dla wszystkich wybranych elementów.

Właściwości, które nie obsługują takiego zachowania, są w przypadku wielokrotnego wyboru wyświetlane jako disabled.

Debugowanie układu

Aby analizować błędy układu w aplikacjach, graficzny debugger udostępnia różne narzędzia pomocnicze.

Weryfikacja granic elementów

Rozmiar (relatywnej) pozycji elementu można odczytać bezpośrednio we właściwościach VisualElement (offset X, offset Y, width oraz height). Często jednak łatwiej jest ustawić tło elementu na widoczny kolor. W ten sposób bardzo szybko można rozpoznać, czy element ma nieprawidłowy rozmiar lub czy wypełnia cały dostępny obszar kontenera (np. ScrollPane).

Wskazówka
Tło wielu elementów, w szczególności prostych kontenerów takich jak View, jest przezroczyste. Jeśli w takich elementach ma być wyświetlany kolor tła, w wyborze koloru należy ustawić wartość cyan.

Zmiana wskazówek dla menedżera układu

Za pomocą trzech właściwości maximum width, maximum height oraz minimum height można wpływać na zachowanie różnych menedżerów układu (szczególnie BoxLayout). Interesujące są w szczególności następujące wartości:

  • 0 — określa, że element nie może zostać powiększony powyżej swojej preferowanej wysokości
  • 32767 (Short.MAX_VALUE) — określa, że element może zostać powiększony dowolnie
  • -1 — można w ten sposób przywrócić wartość domyślną (Default)

Podobne możliwości istnieją również dla właściwości minimum width oraz minimum height.

Wyświetlanie linii prowadzących

Jeśli w układach wielokolumnowych pojawiają się problemy z prostym wyrównaniem pól, przyczyną często jest nieprawidłowe (wielokrotne) użycie linii prowadzących (Guides). Linie prowadzące powinny być zawsze definiowane tylko raz — w odniesieniu do hierarchii kontenerów możliwie jak najwyżej.

Jednokrotna definicja linii prowadzących jest szczególnie przydatna np. wtedy, gdy w obszarze zapytań potrzebny jest układ dwukolumnowy, a w obszarze wyników układ pięciokolumnowy.

Wskazówka
W pakiecie com.cisag.pgm.gui linie prowadzące mogą być tworzone pośrednio poprzez użycie konstruktora com.cisag.pgm.gui.StandardLayout z parametrem różnym od zera. W tym przypadku nie jest to jednak zalecane. Zamiast tego linie prowadzące powinny być definiowane podczas dodawania kontenera za pomocą metody setGuides.

Aby sprawdzić, które kontenery zawierają linie prowadzące (lub są „standardowymi” kontenerami z Layout-Manager), można wyświetlić je w graficznym debuggerze, zaznaczając pole wyboru paint guides. Wyszukiwanie rozpoczyna się od aktualnie wybranego elementu w hierarchii kontenerów i trwa do momentu znalezienia elementu z definicją linii prowadzących. W tym elemencie linie prowadzące zostają następnie wyświetlone.

Dla każdej kolumny można zdefiniować do sześciu linii prowadzących, które mogą być wykorzystywane w następujący sposób:

  • początek kolumny (jasnoczerwony)
  • początek etykiety (ciemnoczerwony)
  • koniec etykiety (ciemnoczerwony)
  • początek pola (ciemnoczerwony)
  • koniec pola (ciemnoczerwony)
  • koniec kolumny (niebieski)

Szczególny przypadek występuje, którego nie należy stosować: View znajduje się w SubShelf. Zarówno View, jak i SubShelf używają menedżera układu StandardLayout. Jak można zobaczyć na ilustracji, linie prowadzące nie są wówczas identyczne, lecz w Shelf są definiowane ponownie.

Weryfikacja sterowania fokusem

Za pomocą przycisku request focus można przypisać fokus klawiatury do elementu. Może to być przydatne np. wtedy, gdy w kontenerze ma zostać sprawdzone, który z jego elementów podrzędnych otrzyma fokus klawiatury.

Usuwanie elementów

Za pomocą przycisku remove element możliwe jest również usuwanie elementów.

Uwaga
Usuwanie elementów może prowadzić do poważnych błędów w powiązanej aplikacji.

Czy ten artykuł był pomocny?