Dostosowywanie rozmiaru formularza (zoomowanie) do rozmiaru ekranu

1

Nietypowe pytanie dziś mam, na które Google nie zna odpowiedzi, a przynajmniej nic na ten temat nie znalazłem; Ciężko to cholerstwo opisać, jednak spróbujmy;

Załóżmy, że mam formularz z jakąś zawartością i zajmuje on dokładnie 90% wysokości i 90% szerokości ekranu (z marginesami ze wszystkich stron); Zaprojektowany jest na ekranie o rozdzielczości załóżmy 1024x768; Jeżeli uruchomię program, na ekranie zobaczę formularz z odpowiednimi wymiarami; Natomiast jeśli uruchomię program na ekranie, który ma większą rozdzielczość, wymiary formularza zostaną takie same (w pikselach), jednak procentowo formularz zajmować będzie mniej niż 90% szerokości i wysokości ekranu i to jest normalne;

Jeżeli w kodzie programu zapiszę instrukcje, które dostosują rozmiar okna do rozdzielczości, okno zawsze będzie zajmować 90% szerokości i wysokości, ale zmienią się wymiary liczone w pikselach - to także jest normalne;

Interesuje mnie inny efekt; Chciałbym aby formularz zawsze zajmował przykładowo 90% szerokości i wysokości ekranu, ale żeby jego pikselowe wymiary nie zmieniły się; Jeśli proporcje ekranu są inne (np. zamiast 4:316:9) to zmieni się rozmiar marginesów po bokach i procentowa szerokość formularza; Chodzi o taki sam efekt, jaki obsługiwany jest przez tryb graficzny - bez względu na proporcje ekranu i jego typ, możliwe jest uruchomienie trybu graficznego np. 800x600 i obraz zawsze rozciągany jest co najmniej na pełną wysokość ekranu (zmienia się DPI);

Interesujący mnie efekt ilustruje poniższy obrazek:

screens.png

Rozmiar okna dostosowywany jest do rozdzielczości, proporcje formularza oraz zawartości pozostają niezmienne;

Czyli podsumowując, chodzi o efekt zoomu - program pobierałby rozdzielczość ekranu i odpowiednio zoomował formularz i jego zawartość; Jest to zupełnie coś innego niż skalowanie, bo zawartość okna nie była by rozciągana (zmieniając pikselowe wymiary komponentów), a powiększana na ekranie, według obliczonego mnożnika; Taki efekt wykorzystują m.in. aplikacje Flash - bez względu na rozdzielczość ekranu (i wymiary obszaru roboczego przeglądarki), zawsze dostosowują swój rozmiar, zachowując oryginalne proporcje elementów aplikacji;

Czy takie coś jest w ogóle możliwe? Zoomowanie okna zamiast powiększania jego pikselowego rozmiaru, bez zmiany rozdzielczości ekranu?

Wiem, że brzmi to kosmicznie, jednak może istnieje jakiś sposób na wykonanie tego.

0

Z tego co mi się wydaje, to nie jest coś takiego możliwe. Przecież jeśli masz stały rozmiar w pikselach, to silą rzeczy przy rożnych rozdzielczościach będziesz miał inne wymiary na ekranie. Jeśli chcesz aby rozmiar został stały będziesz musiał użyć innej jednostki a nie px.

Pytanie tylko dlaczego to jest tak ważne żeby rozmiar w px był stały nawet jak zmienia się wielkość okna?

0

Przecież jeśli masz stały rozmiar w pikselach, to silą rzeczy przy rożnych rozdzielczościach będziesz miał inne wymiary na ekranie.

No tak - to jest standardowe zachowanie i nic w nim dziwnego;

Jeśli chcesz aby rozmiar został stały będziesz musiał użyć innej jednostki a nie px.

Tak, tyle że wtedy co najwyżej zmienię rozmiar formularza i komponentów, jednak pewne elementy pozostaną w normalnych rozmiarach (np. fonty, grafiki itd.);

Pytanie tylko dlaczego to jest tak ważne żeby rozmiar w px był stały nawet jak zmienia się wielkość okna?

Ja tylko pytam czy takie coś jest możliwe, bez odstawiania cudów i pisania tego od podstaw (np. czy jest do tego jakaś gotowa biblioteka); Fajnie by było, gdyby takie coś istniało i dało się po prostu zoomować formularz z całą jego zawartością, również grafikami i fontami;

Jeżeli czegoś takiego nie ma to nic nie szkodzi - trzeba będzie kiedyś takie cuś napisać :]

1

Ok już rozumiem dokładnie o co Ci chodzi. Jest rozwiązanie tego. Tylko w Lazarusie nie ma :(

Delphi oferuje bardzo fajną funkcję jaką jest ScaleBy.

Próbuje ona skalować całe okno łącznie ze wszystkimi kontrolkami zachowując wzajemne ułożenie. Przykładowy kod:

procedure TForm1.Button1Click(Sender: TObject);
begin
  Form1.ScaleBy(2,1);
end;

Jednak ten sposób nie powiększa grafik na komponencie TSpeedButton. Chyba, że coś trzeba ustawić, a ja nie wiem co. Nie mniej jednak TImage ładnie się skaluje.
Tak oto się prezentuje formatka przed wywołaniem ScaleBy
fab9676b5c.png
Natomiast po przeskalowaniu tak:
7041580229.png

Dolny TSpeedButton niestety nie wygląda zbyt ładnie. Ale pozostałe komponenty działają idealnie. I to nie tylko te proste, ale DBGrid też działa.

0

@Mr.YaHooo - ten sposób znam, jednak ScaleBy nie jest idealne, bo pewne elementy wyklucza ze skalowania (np. belka tytułowa okna); No i pozostaje problem samego rozciągania - brak rozmywania obrazu (coś jak w starym Paint);

Na formularzu i tak mam wyłącznie swoje komponenty (łącznie z całym obramowaniem okna), więc spróbuję wypracować takie rozwiązanie, aby całość jakoś powiększyć; Przy czym to i tak w ramach zaspokojenia ciekawości - nie żeby mi to było koniecznie potrzebne :]

0

@Paweł Dmitruk szukałem i jakoś nie znalazłem. Ale dzięki za poprawkę.

@furious programming tak, niestety ScaleBy nie jest idealne i czasem potrafi rozwalić układ komponentów. Raz miałem problem z dwoma komponentami TPanel gdzie oba miały wyrównanie ustawione na alBottom Otóż po wykonaniu owej funkcji... ich kolejność została zamieniona.

Jeśli chodzi o obramowanie okna, to nie mam pojęcia jak tego dokonać. O ile dobrze myślę, to tym steruje sam Windows i to on rysuje przecież okno. Żeby uzyskać zmienną wysokość belki nie wiem czy nie trzeba ręcznie rysować całego okna. Wtedy nie powinno być z tym problemów, ponieważ wtedy da się przecież zrobić dosłownie wszystko.

0

Chciałbym aby formularz zawsze zajmował przykładowo 90% szerokości i wysokości ekranu [...] i obraz zawsze rozciągany jest

Takie skalowanie robi się np. w grach, ale w aplikacji okienkowej typowo „formularzowej” to raczej WTF.
Typowy program raczej rozsuwa elementy zwiększając obszar roboczy (jeżeli w programie jest coś co można nazwać obszarem roboczym).

Program powinien za to dostosowywać się do systemowego DPI, czyli być DPI-aware.

0

To było luźne pytanie i jak widać nikt czegoś takiego nie wymyślił - jeszcze :]

Takie skalowanie robi się np. w grach, ale w aplikacji okienkowej typowo „formularzowej” to raczej WTF.

Niekoniecznie WTF - ~wszystkie gry działające w graficznym trybie pełnoekranowym korzystają z takiego skalowania (automatycznie), nie dbając kompletnie o DPI; Więc jeśli program okienkowy też tak by wyglądał (też używałby takiego skalowania), nie było wcale aż tak źle, jak może się wydawać;

Jedyne co może dziwić to nietypowość takiego rozwiązania, bo samo jego wykorzystanie jak najbardziej ma sens.

0
furious programming napisał(a):

wszystkie gry działające w graficznym trybie pełnoekranowym korzystają z takiego skalowania (automatycznie), nie dbając kompletnie o DPI;
Tak, jednak w przypadku gier odbywa się to w trochę inny sposób. Sama gra wykorzystuje sobie współrzędne, nazwijmy to wewnętrzne w swoim świecie. Dla wygodny mogą to być np. metry. Wszystkie obliczenia robi się korzystając z tych metrów. Dopiero podczas samego renderowania robimy z aktualnego widoku bitmapę o rozdzielczości 640x480 albo 1920x1080 czy jakąkolwiek inną. W przypadku programów okienkowych domyślnie korzysta się z natywnych pikseli i stąd te problemy.

furious programming napisał(a):

Więc jeśli program okienkowy też tak by wyglądał (też używałby takiego skalowania), nie było wcale aż tak źle, jak może się wydawać;
Oczywiście. U siebie mam to rozwiązane za pomocą ScaleBy. User w ustawieniach systemu wybiera o jaki % ma być powiększone okno. Jak ma 1024x768 to nie wybiera. Jak FullHD to wybierze odpowiednio większe powiększenie. O wiele lepsze rozwiązanie niż zwiększanie DPI gdzie przy większych rozdzielczościach i wysokich DPI czasem działa to słabo i niektóre elementy w programach wyglądają jakby był użyty jakiś antyaliasing i teksty są trudne do odczytania.

0

Nie tłumaczcie mi jak to wygląda w grach, bo wiem jak to wygląda i jak działa :]

Dokładnie nad takim samym mechanizmem zastanawiam się, jeśli chodzi o aplikacje okienkowe (które także docelowo miały by działać na pełny ekran, ale nie w trybie graficznym); Dlatego też zapytałem, czy dla aplikacji okienkowych istnieje może jakieś gotowe rozwiązanie (jakaś biblioteka), stanowiące nazwijmy to hooka na renderowanie okna, ale i procedurę obsługi komunikatów, która by dokonywała translacji m.in. współrzędnych myszy dla komunikatów WM_MOUSE*; Ale że nie ma - trudno, nie jest mi to koniecznie potrzebne;

Natomiast ScaleBy i inne znane mechanizmy są ułomne - zawartość okna i jego ramka nie są w całości skalowane; Pewne elementy pozostają w swoich oryginalnych wymiarach (np. grafiki w przyciskach); Gdyby taki wynalazek o jaki pytam istniał, dawałby większe i lepsze możliwości od pozostałych;

Tyle chciałem wiedzieć - dyskusję uważam za wyczerpaną.

0
furious programming napisał(a):

Nie tłumaczcie mi jak to wygląda w grach, bo wiem jak to wygląda i jak działa :]
Oj tam, nie dasz się pochwalić wiedzą :P

furious programming napisał(a):

Dokładnie nad takim samym mechanizmem zastanawiam się, jeśli chodzi o aplikacje okienkowe (które także docelowo miały by działać na pełny ekran, ale nie w trybie graficznym); Dlatego też zapytałem, czy dla aplikacji okienkowych istnieje może jakieś gotowe rozwiązanie (jakaś biblioteka), stanowiące nazwijmy to hooka na renderowanie okna, ale i procedurę obsługi komunikatów, która by dokonywała translacji m.in. współrzędnych myszy dla komunikatów WM_MOUSE*; Ale że nie ma - trudno, nie jest mi to koniecznie potrzebne;
Niestety nie widziałem takiej biblioteki. Chociaż zastanawiam się czy nie można by do tego użyć jakiejś biblioteki typu VCL Skinner. Zasada działania taka sama. Więc jeśli by się znalazła taka o otwartym źródle, to dołożenie skalowania nie powinno być jakimś specjalnie trudnym zadaniem skoro całość renderowania okna już jest napisana.

furious programming napisał(a):

Natomiast ScaleBy i inne znane mechanizmy są ułomne - zawartość okna i jego ramka nie są w całości skalowane; Pewne elementy pozostają w swoich oryginalnych wymiarach (np. grafiki w przyciskach); Gdyby taki wynalazek o jaki pytam istniał, dawałby większe i lepsze możliwości od pozostałych;
Wiem, u mnie jednak to wystarczało i nie musiałem szukać innego rozwiązania.

0

@Mr.YaHooo - i tak małą częścią wiedzy się pochwaliłeś :]

Jeśli o gry chodzi, to sprawa nie jest taka oczywista - nie jedno rozwiązanie istnieje; Jeżeli biorąc pod uwagę gry działające w trybie pełnoekranowym, to rozdzielczość oczywiście można sobie wybrać (choć nie w każdej grze); Sposobów jest kilka - pierwszy, w module ustawień, podczas działania gry; Drugi, za pomocą małego narzędzia, uruchamianego przed rozruchem gry lub zupełnie osobno (stara technika, istniejąca np. w takich grach jak StuntGP, Airfix Dogfighter itd.); Trzeci sposób to partyzantka, czyli ręczna modyfikacja konfigów; Być może istnieją jeszcze inne rozwiązania, ale to nie jest ważne;

Gra podczas rozruchu inicjuje tryb graficzny (za pomocą jakigoś API), ustawiając bieżącą rozdzielczość; To co widzimy na ekranie to po części efekt pracy programu, po części sprzętu/sterowników [potrzebne źródło];

Gra generuje klatki na różne sposoby - możliwe jest wykorzystanie pomocniczej bitmapy z zawartością klatki o dokładnie takim samym rozmiarze jak ustaiona rozdzielczość ekranu trybu graficznego (np. Re-Volt - rozdzielczość 800x600 to generowanie klatek o rozmiarach 800x600);

Drugi sposób to generowanie klatek o mniejszym rozmiarze;

Trzeci sposób jest najciekawszy - wielokrotne buforowanie docelowej bitmapy, co jak przypuszczam (i sam bym implementował) ma miejsce w emulatorach, np. FCEUX dla platformy NES; Pierwszy krok to wygenerowanie surowej klatki o stałym i niezmiennym rozmiarze - dla NES standardowo to 256x240 pikseli; Brak dodatkowych kroków to namalowanie na ekranie tej małej klatki, rozciągniętej na pełen ekran według bieżących filtrów (pikseloza/rozmywanie/zaokrąglanie); Kolejnym krokiem może być namalowanie tej małej bitmapy na większej, używając wspomnianych filtrów; Duża bitmapa może być wykorzystana do namalowania kolejnego efektu, np. scanline'ów lub innych efektów, których nie można użyć na małej bitmapie; Na koniec namalowanie docelowej bitmapy na ekranie - również według ustawień (totalny fullscreen lub wykorzystanie oryginalnego ratio, niezależnie od ustawień sprzętu);

Obstawiam, że typowe gry po prostu używają bitmap-klatek o takim samym rozmiarze jak rozdziałka ekranu w trybie graficznym, więc mało tu zabawy; Resztą zajmuje się sprzęt (głównie jeśli chodzi o matryce panoramiczne i użytą rozdzielczość niepanoramiczną); W każdym razie specem od gamedevu nie jestem, więc mogę się mylić; Jak powszechnie wiadomo - ja tu tylko sprzatam :]

Chociaż zastanawiam się czy nie można by do tego użyć jakiejś biblioteki typu VCL Skinner.

Nie wiem, choć takie rozwiązanie nie może być związane z konkretną biblioteką dla VCL; Po prostu z jednej strony musi być pośrednikiem pomiędzy interfejscem i myszą, a obsługą komunikatów, a z drugiej strony, musi być wrapperem na cały mechanizm zarządzania formularzami i ich renderowaniem; Rozwiązanie zapewne bardzo trudne do zaimplementowania, ale na pewno nie awykonalne;

Więc jeśli by się znalazła taka o otwartym źródle, to dołożenie skalowania nie powinno być jakimś specjalnie trudnym zadaniem skoro całość renderowania okna już jest napisana.

Myślę, że łatwiej było by taki efekt wykonać w przypadku korzystania tylko i wyłącznie z własnych komponentów i własnego obramowania (styl bsNone dla wszystkich okien) - właśnie dlatego zainteresował mnie ten temat; Tyle że komponenty nie były by tak naprawdę standardowymi komponentami, a zwykłymi, niewizualnymi obiektami; Do tego jedna klasa renderująca zawartość formularza (korzystająca z porządnych filtrów, używających anti-aliasingu do malowania rozciągniętych bitmap) oraz funkcja konwertujaca współrzędne myszy we wszystkich komunikatach myszy, na wewnętrznie obsługiwane;

Małym minusem było by cofnięcie się do ery świetności WinAPI - brak designera formularzy, wszelkie teksty, grafiki, zbiory kolorów itd. musiałyby być ładowane z dedykowanych plików lub z zasobów (własny mechanizm, podobny do VCL, ale niezależny);

Może kiedyś spróbuję wykonać próbkę takiego czegoś.

0
furious programming napisał(a):

i tak małą częścią wiedzy się pochwaliłeś :]
Tak, bo to nie jest wątek o grach :]

furious programming napisał(a):

Jeśli o gry chodzi, to sprawa nie jest taka oczywista - nie jedno rozwiązanie istnieje; Jeżeli biorąc pod uwagę gry działające w trybie pełnoekranowym, to rozdzielczość oczywiście można sobie wybrać (choć nie w każdej grze); Sposobów jest kilka - pierwszy, w module ustawień, podczas działania gry; Drugi, za pomocą małego narzędzia, uruchamianego przed rozruchem gry lub zupełnie osobno (stara technika, istniejąca np. w takich grach jak StuntGP, Airfix Dogfighter itd.); Trzeci sposób to partyzantka, czyli ręczna modyfikacja konfigów; Być może istnieją jeszcze inne rozwiązania, ale to nie jest ważne;
Oczywiście, dla mnie takie które przedstawiłem jest po prostu naturalne.

furious programming napisał(a):

Trzeci sposób jest najciekawszy - wielokrotne buforowanie docelowej bitmapy, co jak przypuszczam (i sam bym implementował) ma miejsce w emulatorach, np. FCEUX dla platformy NES; Pierwszy krok to wygenerowanie surowej klatki o stałym i niezmiennym rozmiarze - dla NES standardowo to 256x240 pikseli; Brak dodatkowych kroków to namalowanie na ekranie tej małej klatki, rozciągniętej na pełen ekran według bieżących filtrów (pikseloza/rozmywanie/zaokrąglanie); Kolejnym krokiem może być namalowanie tej małej bitmapy na większej, używając wspomnianych filtrów; Duża bitmapa może być wykorzystana do namalowania kolejnego efektu, np. scanline'ów lub innych efektów, których nie można użyć na małej bitmapie; Na koniec namalowanie docelowej bitmapy na ekranie - również według ustawień (totalny fullscreen lub wykorzystanie oryginalnego ratio, niezależnie od ustawień sprzętu);
Tak, tylko mi się wydaje, że taki sposób generowania grafiki wykorzystywany jest w emulatorach takich urządzeń jak konsole, czy wspomniany przez Ciebie NES. Wynika to ze specyfikacji emulowanego sprzętu gdzie nie było mowy o żadnej zmianie rozdzielczości. A granie w takiego NES'a przy rozdziałce FullHD bez skalowania z 256x240 było by delikatnie mówiąc utrudnione ;)

furious programming napisał(a):

Obstawiam, że typowe gry po prostu używają bitmap-klatek o takim samym rozmiarze jak rozdziałka ekranu w trybie graficznym, więc mało tu zabawy; Resztą zajmuje się sprzęt (głównie jeśli chodzi o matryce panoramiczne i użytą rozdzielczość niepanoramiczną); W każdym razie specem od gamedevu nie jestem, więc mogę się mylić; Jak powszechnie wiadomo - ja tu tylko sprzatam :]
Z tego co ja wiem (mogę się mylić, za dużo w grach doświadczenia nie mam) tak właśnie jest.

Nie wiem, choć takie rozwiązanie nie może być związane z konkretną biblioteką dla VCL; Po prostu z jednej strony musi być pośrednikiem pomiędzy interfejscem i myszą, a obsługą komunikatów, a z drugiej strony, musi być wrapperem na cały mechanizm zarządzania formularzami i ich renderowaniem; Rozwiązanie zapewne bardzo trudne do zaimplementowania, ale na pewno nie awykonalne;
Oj tam, w końcu ktoś to przecież napisał, więc nie może to być tak trudne.

Myślę, że łatwiej było by taki efekt wykonać w przypadku korzystania tylko i wyłącznie z własnych komponentów i własnego obramowania (styl bsNone dla wszystkich okien) - właśnie dlatego zainteresował mnie ten temat; Tyle że komponenty nie były by tak naprawdę standardowymi komponentami, a zwykłymi, niewizualnymi obiektami; Do tego jedna klasa renderująca zawartość formularza (korzystająca z porządnych filtrów, używających anti-aliasingu do malowania rozciągniętych bitmap) oraz funkcja konwertujaca współrzędne myszy we wszystkich komunikatach myszy, na wewnętrznie obsługiwane;

Małym minusem było by cofnięcie się do ery świetności WinAPI - brak designera formularzy, wszelkie teksty, grafiki, zbiory kolorów itd. musiałyby być ładowane z dedykowanych plików lub z zasobów (własny mechanizm, podobny do VCL, ale niezależny);
Czemu? Widziałem gdzieś (nie pamiętam tylko gdzie) komponent dzięki któremu można było wybierać nie tylko kolory ale również styl (MacOS, WindowsNT, WindowsXP,...) rysowanych kontrolek. Działało to ze wszystkimi standardowymi kontrolkami, a również częścią komponentów 3-rd party. Nie wymuszało to odejścia od designera formularzy. Wszystko było załatwione w customowym rysowaniu okna. Popatrz na Qt, tam też można wybierać sobie styl aplikacji, a wszystko robisz z designera.

0

@Mr.YaHooo - IMO nie jest to takie proste i oczywiste;

Jeżeli mam formularz o wymiarach 800x600 px, który na rozdzielczości 1024x768 px wyświetlany jest w oryginalnym rozmiarze (czyli jest to 100%); Teraz jeśli rozdzielczość jest większa, musi zostać nieco rozciągnięty (powiększony); Załóżmy że mnożnik to 1.4, czyli rozmiar okna na takim ekranie to 1120x840 px;

W internalsach formularz nadal musi posiadać rozmiar oryginalny, czyli 800x600 px, jednak na ekranie rozmiar obliczony, czyli 1120x840 px; Nie mogę rozciągnąć standardowo formularza, bo komponenty się powiększą według np. Anchors czy Align; A zostawiając oryginalny rozmiar okna, nie mogę rysować na ekranie większego (tylko tyle, ile wynosi jego rozmiar, bo Canvas nie sięga poza powierzchnię okna);

Aby móc skorzystać z designera i standardowych komponentów, efekt musiałby być tak zaimplementowany, że mam oryginalny formularz na którym wszystko jest malowane, a na ekranie mieć formularz większy niż oryginalny (o obliczonych rozmiarach), na którym po prostu namaluję formę oryginalną, tyle że rozciągniętą; Tak jakby zrobić zrzut ekranu tego okna i tak otrzymaną bitmapę namalować funkcją w rodzaju BitBlt jako większą; Wtedy wszystko się rozciągnie, łącznie z obramowaniem, belką tytułową i systemowymi przyciskami, a także cała zawartość okna, każdy komponent z każdym jego elementem (obramowanie, tekst, obrazki itd.);

W przeciwnym razie efekt będzie taki sam jak ScaleBy; Aby tego uniknąć, po powiększeniu formularza i komponentów, musiałbym znać ich pierwotny rozmiar i położenie, aby móc skorzystać z mnożnika i namalować ich powierzchnię jako wizualnie rozciągniętą.

0

@furious programming faktycznie to nie jest takie proste. Osobiście poszedłbym w stronę napisania własnego ScaleBy, w końcu zwiększenie rozmiarów formularza to chyba nic złego? Popróbowałem trochę i muszę się przyznać, że nic sensownego nie wymyśliłem. Zawsze coś się rozjeżdżało. A bitmapki na buttonach po przeskalowaniu o 30% wyglądały słabo...

1 użytkowników online, w tym zalogowanych: 0, gości: 1