Jedną z zalet C++Buildera, jako narzędzia RAD, jest możliwość łatwego i szybkiego budowania interfejsu użytkownika aplikacji za pomocą użytecznych narzędzi wizualnych. Same narzędzia jednak nie wystarczą – aby interfejs aplikacji spełnił oczekiwania użytkownika i spotkał się z jego akceptacją, konieczne jest kierowanie się pewnymi zasadami jego budowy. Zasady te, wynikające przede wszystkim ze sposobu użytkowania komputera przez (typowego) użytkownika, muszą być znane każdemu projektantowi, jeżeli chce on tworzyć aplikacje obsługiwane w sposób nieskomplikowany, zgodnie z intuicją i wymogami wygody użytkownika.
Rozdział ten rozpoczniemy od przedstawienia ogólnych przesłanek funkcjonowania interfejsu użytkownika i wynikających stąd zasad jego konstrukcji; w pozostałej jego części zaprezentujemy praktyczne zastosowanie tych zasad na przykładzie kilku wybranych aplikacji.
Gdy przyjrzeć się przedstawionym poniżej regułom, określającym zasady budowania interfejsu użytkownika, większość z nich wydaje się być intuicyjnie jasna, ale nie wszystkie są aż tak oczywiste. Ich przestrzeganie jest jednak niezbędne do tego, by interfejs użytkownika – stanowiący zgodnie z nazwą pewną platformę współpracy użytkownika z aplikacją – nie stał się w tej współpracy przeszkodą. Sekwencyjny charakter tekstu wymusza co prawda omówienie wspomnianych reguł w określonej kolejności, jednak każda z nich jest nie mniej ważna od pozostałych.
· Spełnienie oczekiwań użytkownika – na ogólnie rozumianą przydatność aplikacji dla użytkownika składają się nie tylko wykonywane przez nią czynności, lecz również i komfort jej obsługi; ten ostatni zdeterminowany jest przede wszystkim przez jej interfejs użytkownika.
· Przejrzystość i klarowność – aby interfejs nie odwracał uwagi użytkownika od zasadniczych czynności spełnianych przez aplikację, jego wygląd powinien pozostawać w ścisłym, intuicyjnie jasnym związku ze spełnianymi przez niego funkcjami. Elementy wykonujące powiązane ze sobą funkcje powinny być w widoczny sposób pogrupowane, należy jednak unikać nadmiernego ich zagęszczenia; nawigacja pomiędzy różnymi częściami interfejsu powinna odbywać się w prosty sposób. Kontrolki obsługiwane myszą powinny mieć rozmiary wystarczająco duże, my można było w nie „trafić” kursorem bez kłopotu, a kontrolki używane szczególnie często jedna po drugiej nie powinny znajdować się w zbyt dużej odległości od siebie ze względów ergonomicznych – dla użytkownika nie ma nic bardziej męczącego niż kłopotliwe pozycjonowanie myszy na mikroskopijnych przyciskach i „dzikie” skoki kursora przez połowę ekranu.
· Intuicyjność i logika – poszczególne funkcje oferowane przez interfejs użytkownika powinny być dostępne w oczywisty, intuicyjny sposób, nie wymagający wykoncypowanych przemyśleń ani też zaawansowanych kwalifikacji. W przeciwnym razie aplikacja może stać się trudna w obsłudze dla początkującego użytkownika i może nie zostać przez niego zaakceptowana, mimo wysokiego stopnia zaawansowania spełnianych przez nią funkcji.
· Przyjazność – interfejs skonstruowany w przyjazny dla użytkownika sposób wymaga mniej czasu na nauczenie się jego obsługi, jak również mniej wysiłku przy samej obsłudze.
· „Sprzężenie zwrotne” – użytkownik powinien mieć pewność, iż oczekiwane przez niego zachowania aplikacji rzeczywiście występują; upewnieniu takiemu służyć mogą różnorakie komunikaty potwierdzające wykonanie różnych operacji, czy też informujące o stopniu zaawansowania operacji długotrwałych, chociaż przejawy owego „sprzężenia zwrotnego” przybierają niekiedy postać bardziej elementarną, jak np. zmiana wyglądu przycisku po jego „naciśnięciu” czy sygnał dźwiękowy w reakcji na niewłaściwy klawisz albo kliknięcie w niewłaściwym miejscu – mimo swej elementarności środki takie nie powinny być jednak traktowane marginalnie. W szczególnych przypadkach interfejs może oferować użytkownikowi dodatkowe środki informacyjne, jak np. migotanie paska tytułowego czy obrzeża ekranu towarzyszące sygnałowi dźwiękowemu, przydatne np. dla osób z upośledzeniem słuchu.
· Dostępność – poszczególne funkcje interfejsu powinny być dostępne w możliwie największym stopniu – oprócz klawiatury i myszy może okazać się uzasadnione zaimplementowanie alternatywnych środków wprowadzania informacji, jak np. sterowanie głosem przeznaczone dla użytkowników z utrudnieniami obsługi standardowych urządzeń wejściowych.
· System pomocy – ze względu na mnogość funkcji realizowanych przez typowy interfejs użytkownika wielu ich aspektów nie da się wyrazić wprost poprzez konstrukcję samego interfejsu, bez względu na to, jak wysoce intuicyjny i przyjazny by on nie był; uzupełniającą rolę w tej mierze spełniają wbudowane w aplikację systemy pomocy (online help) oraz dokumentacja i ew. obsługa techniczna – „gorąca linia” lub poczta elektroniczna.
· Konfigurowalność – należy umożliwić przystosowywanie rozmaitych aspektów wyglądu i działania interfejsu do indywidualnych potrzeb użytkowników. Przykładem takiego przystosowywania może być dobór kolorystyki poszczególnych elementów, lecz pojęcie konfigurowalności interfejsu nie ogranicza się bynajmniej do samego jego wyglądu – różni użytkownicy mogą korzystać z różną częstotliwością z różnych funkcji aplikacji, należy zatem pozwolić im na takie skonfigurowanie elementów interfejsu, by funkcje używane najczęściej dostępne były jak najszybciej i uruchamiane w jak najprostszy sposób.
· Możliwość odwrotu – nikt z nas nie jest nieomylny; użytkownicy komputerów popełniają więc błędy i możliwość anulowania skutków błędnej operacji czy wycofania się z błędnie wybranej opcji może mieć niekiedy kluczowe znaczenie. Przykładem tego rodzaju mechanizmów są powszechnie stosowane funkcje „Cofnij” (w wersji angielskiej „Undo”), a także wycofywanie się na poprzedni poziom dialogu za pomocą klawisza (najczęściej) Esc.
· Czytelna informacja o błędach – niezależnie od tego, jak „idiotoodporna” byłaby aplikacja, nie jest ona w stanie wykonać bezsensownych żądań użytkownika, ani też zaradzić różnorodnym „nieszczęściom” zewnętrznym w rodzaju niewystarczającej pamięci czy błędów zapisu na dysk. Sytuacje takie powinny być komunikowane użytkownikowi w sposób zrozumiały, wyrażony w kategoriach, w jakich postrzega on funkcjonowanie programu; niestety, wiele komunikatów o błędach ma tak lakoniczną formę i tak niezrozumiałą treść, iż nawet sami autorzy aplikacji (!) mają po pewnym czasie trudności z ich interpretacją i określeniem prawdziwej przyczyny błędu. W niektórych sytuacjach błąd ma charakter przejściowy i ponowna próba wykonania danej czynności może zakończyć się pomyślnie – czego przykładem może być brak dostępu do zablokowanego pliku w sieci – niektóre błędy, jak np. brak dyskietki w napędzie, wynikają też z przeoczenia użytkownika. Porządnie skonstruowana aplikacja powinna każdorazowo informować o takich uwarunkowaniach.
· Symbolika graficzna – rozmaite symbole, ikony, zróżnicowana kolorystyka itp. czynią interfejs użytkownika bardziej intuicyjnym i łatwiejszym w obsłudze. Nie należy zapominać o oczywistym fakcie, iż postrzeganie symboli odbywa się znacznie szybciej niż odczytywanie napisów, ponadto określone funkcje aplikacji zazwyczaj łatwiej bywają kojarzone z symbolami graficznymi niż z opcjami menu. Te ostatnie są co prawda bardziej komunikatywne dla użytkownika rozpoczynającego pracę z aplikacją, lecz po rozpoznaniu poszczególnych jej funkcji będzie on najprawdopodobniej kojarzył te funkcje z poszczególnymi symbolami. Zróżnicowana kolorystyka może przyczynić się do lepszego uwypuklenia poszczególnych elementów funkcjonalności interfejsu, na przykład przez pogrupowanie powiązanych ze sobą opcji czy przycisków.
· Mobilność – poszczególne elementy interfejsu powinny być dostępne bez zbędnej fatygi użytkownika. Jeżeli przykładowo przypiszemy klawisze skrótu do poszczególnych opcji menu, uwolnimy być może użytkownika od ciągłego „przerzucania się” z klawiatury na mysz i vice versa. Zwiększy się przez to mobilność aplikacji, rozumiana jako ilość wykonanej przez nią pracy w przeliczeniu na wysiłek użytkownika.
Jak już napisaliśmy, większość z przedstawionych zasad wydaje się niemal oczywista, dlatego też często zapomina się o nich i nie od rzeczy było przypomnieć je tutaj – spędzając długie godziny nad debuggerem lub studiowaniem kodu źródłowego zapominamy niekiedy, iż aplikacja nasza trafić może do rąk człowieka nie tylko posługującego się innym językiem, lecz być może nie umiejącego wskazać naszego kraju na mapie…
Jeden przykład wart jest tysiąca słów, więc po zwięzłym przedstawieniu zasad, jakimi rządzi się nowoczesny interfejs użytkownika, pora teraz pokazać, jak zasady te wykorzystywane powinny być w praktyce. W tym celu przedstawimy kilka przykładowych projektów i w kolejnych podrozdziałach omawiać będziemy poszczególne aspekty ich funkcjonowania, oczywiście ze szczególnym uwzględnieniem ich związku z interfejsem użytkownika.
Najczęściej odwoływać się będziemy do projektu realizującego prosty kalkulator – jego kompletny materiał źródłowy znajduje się na dołączonej do książki płycie CD-ROM. Niektóre podrozdziały, omawiające zagadnienia nie związane bezpośrednio z tym projektem, ilustrowane będą dodatkowymi aplikacjami, których wykaz – w kolejności pojawiania się w treści rozdziału – przedstawia tabela 3.1.
Tabela 3.1. Wykaz uzupełniających aplikacji demonstracyjnych używanych w tym rozdziale
Projekt
Wykorzystywany w podrozdziałach…
Focus.bpr
Kontrola migracji skupienia pomiędzy elementami interfejsu
MDIProject.bpr
Dostosowanie tła formularza głównego MDI
Scentralizowane sterowanie akcjami obiektu
Panels.bpr
Wyrównanie – właściwość Align
Zakotwiczenie – właściwość Anchors
Ograniczenia swobody zmiany rozmiarów – właściwość Constraints
ProgressCursor.bpr
Wykorzystanie komponentów TProgressbar i TCGauge
Wygląd kursora
ScreenInfo.bpr
Zróżnicowane konfiguracje graficzne
Wśród wymienionych w tabeli projektów uzupełniających jeden z nich – MDIProject.bpr – wyraźnie odróżnia się od pozostałych, gdyż realizuje wielodokumentowy model interfejsu użytkownika (ang. MDI – Multiple Document Interface), podczas gdy pozostałe należą do kategorii aplikacji jednodokumentowych (ang. SDI – Single Document Interface). Aplikacje jednodokumentowe posiadają jeden formularz główny i opcjonalnie dowolną liczbę formularzy pomocniczych, z których każdy może być wyświetlany w sposób modalny lub niemodalny; owe formularze pomocnicze nie są jednak związane z formularzem głównym relacjami „potomny – rodzicielski” (ang. child – parent). W przypadku aplikacji wielodokumentowej formularze pomocnicze są formularzami potomnymi w stosunku do formularza głównego, co w pierwszym rzędzie uwidacznia się w ten sposób, iż obszar ich widoczności ograniczony jest do obszaru klienta formularza głównego – innymi słowy formularz główny spełnia rolę swoistego „kontenera wizualnego” dla swych formularzy potomnych. Formularze potomne wyświetlane są w sposób niemodalny, co umożliwia łatwe przełączanie się pomiędzy nimi. Funkcjonalność aplikacji wielodokumentowej charakteryzuje się kilkoma unikatowymi cechami, m.in. możliwością rozmaitego aranżowania układu formularzy potomnych, łączenia menu formularza głównego z menu formularza potomnego (ang. menu merging) – można je prześledzić, studiując wspomniany projekt MDIProject.bpr.
Projekt MiniCalculatorProject.bpr realizuje aplikację oferującą podstawowe funkcje prostego kalkulatora, poszerzone o elementy charakterystyczne dla typowej aplikacji dla Windows. To ważny aspekt projektowy – interfejs użytkownika skonstruowany w konwencji (zrozumiałej dla wszystkich) obsługi kalkulatora uwalnia jednocześnie użytkownika od wielu uciążliwości związanych z tą obsługą; zajmiemy się dokładniej tym zagadnieniem w jednym z następnych podrozdziałów.
Rysunek 3.1 przedstawia widok działającego programu, oczywisty dla każdego, kto choć raz w życiu posługiwał się kalkulatorem, nietrudno jednak zauważyć dodatkowe elementy charakterystyczne nie dla samego kalkulatora, lecz właśnie dla jego realizacji w postaci aplikacji dla Windows.
Rysunek 3.1. Kalkulator jako aplikacja dla Windows
W projekcie MiniCalculatorProject.bpr wykorzystano wyłącznie rodzime komponenty C++Buildera – bez uciekania się do komponentów kategorii third-party – co samo w sobie stanowi niezłą ilustrację jego możliwości. Użyte komponenty, w podziale na zawierające je strony palety komponentów, przedstawione zostały w tabeli 3.2.
Tabela 3.2. Komponenty wykorzystane w projekcie MiniCalculatorProject.bpr
Strona palety komponentów
Komponenty
Standard
TActionList
TMainMenu
TPanel
TPopupMenu
Additional
TApplicationEvents
TBevel
TBitBtn
TControlBar
ITImage
TSpeedButton
Win32
TImageList
TStatusBar
Dialog
TColorDialog
Podczas projektowania aplikacji spory wysiłek włożony został w nadanie jej odpowiedniego wyglądu graficznego, w szczególności – w wykonanie niezbędnych do tego ikon i obrazków; posłużono się przy tym pakietem Paint Shop Pro firmy JASC, którego ewaluacyjna wersja 7.00 znajduje się na dołączonej do książki płycie CD-ROM.
UWAGA – w tym miejscu jest mowa o tym, iż na CD-ROMie znajduje się evaluate version PaintShop Pro 7.00
Idea sprzężenia zwrotnego realizowana jest w aplikacjach Windows za pomocą rozmaitych środków, udostępniających użytkownikowi informacje na temat stanu aplikacji lub stopnia zaawansowania wykonywanych przez nią operacji. Do środków tych należą m.in. wszelkiego rodzaju komunikaty (message boxes), paski statusowe (status bars), rozmaite odmiany pasków i wskaźników postępu (progress bars, gauges), a także podpowiedzi (hints) czy sugestywny wygląd kursora, sugerujący użytkownikowi możliwość wykonania określonych operacji w kontekście wskazywanej aktualnie przez ten kursor kontrolki – na przykład kursor w kształcie dłoni sugeruje najczęściej, iż mamy właśnie do czynienia z hiperłączem.
W kolejnych punktach przyjrzymy się dokładniej zasadom funkcjonowania wymienionych przed chwilą komponentów.
Paski postępu (progress bars) stanowią wygodne narzędzie uwidaczniania stopnia zaawansowania długotrwałych operacji wykonywanych przez aplikacje; w niektórych sytuacjach są one jedynym świadectwem tego, iż dana aplikacja wciąż funkcjonuje, a zlecone jej zadanie, chociaż powoli, wciąż jest realizowane.
Reprezentujący pasek postępu komponent TProgressBar znajduje się na stronie Win32 palety komponentów. Jej strona Samples również zawiera komponent tego rodzaju o nazwie TCGauge. Jest on nieco bardziej zaawansowany od swego kolegi, może bowiem przybierać różne formy geometryczne, zaś wyświetlana przez niego informacja ma bardziej czytelną postać i może zostać wyrażona w formie liczbowej. Segmentowana, „skokowa” natura paska TProgressBar wydaje się nie mieć odniesienia do posuwającej się wciąż naprzód operacji, a wyświetlana przezeń informacja umożliwia co najwyżej szacunkowe określenie ilości wykonanej przez aplikację pracy.
Tym niemniej operacje wykonywane przez obydwa komponenty są do siebie podobne. Zakładając, iż komponenty te noszą w naszej aplikacji nazwy (odpowiednio) ProgressBar i CGauge, możemy ustawić wartości odpowiadające ich wskazaniom minimalnym i maksymalnym oraz bieżącej pozycji:
// minimum
ProgressBar–>Min = 0;
CGauge–>MinValue = 0;
// maksimum
ProgressBar–>Max = 0;
CGauge–>MaxValue = 0;
// bieżąca pozycja
ProgressBar–>Position = 0;
CGauge–>Progress = 0;
Możemy także zwiększyć o 1 wskazanie każdej z kontrolek:
// inkrementacja
ProgressBar–>Position = ProgressBar–>Position + 1;
CGauge–>Progress = CGauge–>Progress + 1;
W przypadku obydwu omawianych komponentów próba zwiększenia bieżącego wskazania poza wartość maksymalną nie daje żadnego efektu. TProgressBar dysponuje jednak metodami StepIt() oraz StepBy(), dokonującymi zwiększenia bieżącego wskazania o (odpowiednio) wartość właściwości Step lub specyfikowaną wartość typu int bez respektowania tegoż ograniczenia: wskazanie nie zatrzymuje się na wartości maksymalnej, lecz „zawija się” wokół wskazania minimalnego.
Porównanie możliwości komponentów TProgressBar i TCGauge jest treścią projektu ProgressCursor.bpr, znajdującego się na załączonej płycie CD-ROM. Wybór konkretnej kontrolki jest każdorazowo kwestią konkretnego zastosowania i preferencji projektowych, w każdym razie aplikacja realizująca długotrwałe operacje musi być wyposażona w jakikolwiek element interfejsu, komunikujący użytkownikowi swe funkcjonowanie.
Zmiana wyglądu kursora myszy stosownie do jego położenia i bieżącego stanu aplikacji jest powszechnie stosowanym elementem współczesnych interfejsów użytkownika. W aplikacjach tworzonych z użyciem C++Buildera zmiany wyglądu kursora dokonywać można w dwojaki sposób.
Pierwszy z nich polega na zmianie właściwości typu TCursor kontrolki, w obszarze której kursor przybierać ma określony wygląd – i tak np. właściwość Cursor określa wygląd kursora w sytuacji jego znalezienia się na kontrolce w „zwykłych” warunkach, zaś właściwość DragCursor odpowiedzialna jest za wygląd kursora podczas „przeciągania” kontrolki.
...
bzyk268