GWT ile custom scrollbar programlama

GWT (Google Web Toolkit) son yıllarda Google’ın yaptığı önemli işlerden bir tanesi ve benim de kullanmaktan büyük keyif aldığım popüler web teknolojilerinden bir tanesi. Uzun yıllar boyunca, Microsoft teknolojileri ile yazılım geliştiren benim gibi bir yazılım geliştirici için çok ilginç ve bir o kadar da keyifli bir alan GWT. Son 1,5-2 yıldır rotamı büyük ölçüde Java’ya çevirdim (GWT temelde Java tabanlı çünkü) ve bu kararımın şu ana kadar verdiğim en isabetli kararlardan birisi olduğunu düşünüyorum. (Microsoft teknolojilerini de takip ediyorum ve yoğun bir şekilde kullanmaya devam ediyorum hala, bkz. 15saniye.com, crexta.com)

Uzun zamandır GWT ile ilgili bir şeyler yazmayı planlıyordum, iş-güç derken vakit bulamadım hiç. Her ne kadar çok kolay bir işmiş gibi görünse de uzaktan, blogum için yazı yazmak gerçekten vaktimi alan bir aktivite (ayrıca çok ta keyifli). Çok vaktimi alıyor, çünkü, yazılarımı sırf burada yer işgal etsinler diye değil, paylaştığım insanların gerçekten faydalanabileceği ve kolaylıkla anlayabileceği şekilde hazırlamaya çok dikkat ederek yazıyorum. Bazı yazılarımı hazırlamam günler, hatta haftalar sürebiliyor bu nedenle. Mesela bu yazıyı yazmayı yaklaşık 7-8 ay önce planladım, yaklaşık son bir aydır üzerinde çalışarak taslak haline getirdim ve şimdi de buraya aktarıyorum 🙂

Web uygulamalarında görsel bütünlük uygulamanın anlaşılabilirliği ve kullanım kolaylığı açısından çok önemlidir. Uygulama için seçilmiş olan ana renklerden, kullanılan düğmelerin gölgelerine ve kenarlıklarına kadar her şey tek tek çok önemlidir ve tüm detaylar üzerinde büyük bir önemle durmak gerekir. Tüm bu detaylar üzerinde harcanan vakit kesinlikle bir kayıp değil, uzun vadede uygulama için yapılmış en büyük yatırımlardan birisidir. Bu düşünce ile ortaya çıkartılan ürünlerin başarılı olma şansı çok daha yüksektir.

Günümüzde kullanılan popüler web tarayıcılarının (Firefox, Chrome, Safari, Internet Explorer!) temel motorları ile birlikte HTML ve CSS‘in gücü, yazılımcı ve tasarımcı arkadaşlara, yukarıda önemle üzerinde durduğumuz görsel bütünlüğün sağlanması adına çok esnek yöntemler ve işlevsel fonksiyonlar sunmaktadır. Ancak bazı durumlarda, tarayıcıların içerisinde gömülü olarak yer alan bazı bölümlerin, uygulamanın genel görsel şablonuna uyarlanması çok güçtür. Bazı web tarayıcılarının kaydırma çubuklarının, görsel tasarımlarının değiştirilebilmesinin çok zor olması gibi…

Tasarım açısından, günümüz web teknolojilerini kullanarak tarayıcıların kaydırma çubukları kısmen de olsa değiştirilebilse de, yazılımcı ve tasarımcı arkadaşlar, bu konuda tam olarak görsel bir hakimiyet oluşturma noktasında, yukarıda bahsettiğimiz zorlukları fazlasıyla yaşmaktadırlar. İşte bu yazıyı yazmamın nedeni de özel (custom) olarak programlanacak bir kaydırma çubuğu (scrollbar) ile yazılım ve tasarım açısından tam bir hakimiyet oluşturarak, önemle üzerinde durduğumuz görsel bütünlüğün sağlanması adına, yaşanan bu zorluğu ortadan kaldırmaktır.

Öncelikle programlanacak olan temel yapıyı ana hatlarıyla ortaya koyalım. Kaydırma çubukları nedir ve ne işe yararlar?

Şekil 1. Kaydırma çubuğu (scrollbar) nedir ve ne işe yarar?

Yukarıdaki ekran görüntüsünde de görebileceğimiz gibi kaydırma çubukları bir “içerik alanı” gerektirirler. Kaydırma çubuklarına, içerik alanında yer alan içeriğin, içerik alanına sığmadığı (içeriğin yatay ya da dikey olarak alanın dışına taştığı) durumlarda ihtiyaç duyulur ve taşma yönüne bağlı olarak içeriğin görünürlüğünü sağlarlar. Burada, hepimizin bildiği bu bilgileri yeniden hatırlatmamım sebebi, yazımızın ilerleyen bölümleri için bu bilgilerin hayati önem taşımasıdır.

Genel olarak kaydırma çubuğu ile ilgili bilgilerimizi verdik. Şimdi ise bir kaydırma çubuğuna detaylı olarak bir göz atalım.

Şekil 2. Kaydırma çubuğunun genel bölümleri

Şekil 2, en kaba haliyle bir kaydırma çubuğunun genel bölümlerini ve detaylarını göstermektedir. Bu bölümü çok fazla detaylandırmaya gerek yok diye düşünüyorum çünkü bir çoğumuz, bir şekilde, tarayıcılar (ve diğer yazılımlar) yoluyla, bu konuyu şu veya bu şekilde biliyoruz. Bir kaydırma çubuğu 3 ana bölümden oluşur; içeriği yukarı-aşağı ya da sağ-sol kademeli bir şekilde hareket ettirecek olan düğmelerimiz ve içeriği kademesiz(ya da kademeli) olarak, bağımsız hareket ettirebileceğimiz kaydırma alanı ve kaydırma çubuğu.

Bu arada hemen bir hatırlatma yapmak istiyorum; size bu yazımda dikey bir kaydırma çubuğunun GWT ile nasıl programlanacağını anlatacağım ve göstereceğim ancak paylaşacağım kodlar üzerinde minik değişikliklerle bu kaydırma çubuğunu hem yatay hem de dikey olarak işlev görebilecek şekilde programlamanız elbette mümkün 🙂

Şimdi ise Şekil 1’de paylaştığımız ekran görüntüsünü, kaydırma çubuğunun genel programlama mantığını anlamak adına detaylandıralım;

Şekil 3. Kaydırma çubuğunun görsel elemanlar içerisindeki yeri

Programlanacak olan kaydırma çubuğunun ana hatlarıyla programlanma mantığı şu şekilde olmalıdır;

  • İçerik alanını belirleyen HTML elemanımızın (DIV, SPAN vs.) boyunun, içerisinde yer aldığı ana konteyner (tam olarak uygun bir Türkçe karşılık bulamadım, özür…) elamanımızın boyunu aştığı durumlarda, kaydırma çubuğumuz, aşma yönüne uygun şekilde, yatay ya da dikey olarak otomatik olarak görünür hale gelmelidir. Aksi durumda ise kendisini otomatik olarak gizlemelidir.
  • Kaydırma alanı içerisinde yer alan kaydırma çubuğunun (bkz. Şekil 2 lütfen) boyunu ve kaydırma alanı boşluğuna tıklandığında hangi miktarda içeriğin hareket ettirileceği bilgisini, içerik alanının ana konteynerdan (yine özür…) taşan kısmını göz önünde bulundurarak otomatik olarak yapabilmeliyiz.
  • Kaydırma çubuğununun yukarı-aşağı ya da sağ-sol hareketine göre, içerik alanının ana konteyner içerisindeki pozisyonunu otomatik olarak değiştirmeliyiz.

Ben görsel anlatımın gücüne inananlardanım, bu nedenle bu bilgileri bir de görsel olarak vurgulamak ve örneklemek istiyorum.

Şekil 4. Kaydırma çubuğunun genel çalışma prensibi

Şekil 4, bir kaydırma çubuğunun genel çalışma prensibini göstermektedir. Dikey bir kaydırma çubuğu programlanacağı için görseli de ona göre hazırladım ancak bu temel prensip(ler) yatay kaydırma çubuğu için de birebir geçerli tabii ki.

Kaydırma çubuğunu programlarken dikkatle ele almamız gereken nokta, kaydırma çubuğunun boyunu (dolayısıyla kaydırma alanı içerisindeki boşluğu) ve kaydırma çubuğunun kademesini (kaydırma alanı boşluğuna tıklandığında hangi miktarda içeriğin hareket ettirileceği) belirleyebilmek. Bu konuyu ilerleyen bölümlerde örnek kodlarla daha detaylı inceleyeceğiz.

Bunların dışında kaydırma çubuğu programlanırken dikkat edilmesi gereken bir diğer nokta ise olaylardır (events). Kabaca bu olayların üzerinden de geçelim;

  • Kaydırma çubuğunun içerisinde bulunduğu pencerenin boyutu değiştirildiğinde (resize), kaydırma çubuğu yukarıda bahsettiğimiz ilgili hesaplamaları yaparak kendisini otomatik olarak yeniden boyutlandırmalıdır (window resize event).
  • Fare ile, kaydırma çubuğu tutulup aşağı ve yukarı hareket ettirildiğinde, kaydırma çubuğu ve içerik te farenin hareket yönüne göre hareket etmelidir (mouse down, mouse move events)
  • Fare ile, kaydırma çubuğundaki boş bir alana tıklandığında, kaydırma çubuğu ve içerik tıklanan bölgenin yönüne ve konumuna göre hareket etmelidir (mouse click event)
  • Fare ile, kaydırma çubuğunun yukarı-aşağı ya da sağ-sol bölümlerine tıkladığımız anda kaydırma çubuğu ve içerik, ilgili yönde hareket etmelidir ve yine fare ile, bu bölümlere basılı tutulduğunda içerik otomatik ve sürekli olarak hareket etmelidir (mouse click, mouse down, mouse up events)
  • İçerik alanı içerisinde, farenin tekerlekleri kullanıldığında, içerik ve kaydırma çubuğu, yukarı-aşağı ya da sağ-sol hareket ettirilebilmelidir (mouse wheel event).

Görüldüğü üzere, kaydırma çubuğumuzu programlarken bir çok olayı göz önünde bulundurmamız ve tüm bu olayları birbirleri ile koordineli bir şekilde sorunsuz olarak çalıştırabilmemiz gerekiyor.

Öncelikle kaydırma çubuğumuz bir çok fare olayı içereceği için, kaydırma çubuğumuzu FocusPanel nesnesini inherit (extend) ederek programlamaya başlamamız çok yerinde bir karar olacaktır.

Kaydırma çubuğuna ilişkin genel sınıf yapısı aşağıdaki gibi olacaktır;

public class ScrollBar extends FocusPanel implements
HasScrollBarValueChangedEventHandler, HasScrollBarVisibilityChangedEventHandler,
HasScrollBarStatusChangedEventHandler {
 
	private FlowPanel main = new FlowPanel();
 
	private FocusPanel wrapper = new FocusPanel();
 
	private FlowPanel container = new FlowPanel();
 
	private SimplePanel top = new SimplePanel();
 
	private SimplePanel middle = new SimplePanel();
 
	private SimplePanel bottom = new SimplePanel();
 
	private Label up = new Label();
 
	private Label down = new Label();
 
	private FocusPanel scroll = new FocusPanel();
 
	private boolean initialized = false;
 
	public ScrollBar() {
		setStyleName("gwt-ScrollBar");
 
		setVisible(false);
 
		up.addStyleName("up");
		down.addStyleName("down");
		container.addStyleName("container");
		main.addStyleName("main");
		scroll.addStyleName("scroll");
		top.addStyleName("top");
		middle.addStyleName("middle");
		bottom.addStyleName("bottom");
 
		container.add(top);
		container.add(middle);
		container.add(bottom);
 
		wrapper.add(container);
		scroll.add(wrapper);
 
		main.add(up);
		main.add(scroll);
		main.add(down);
 
		add(main);
	}
}

Kaydırma çubuğumuza ait sınıfımızı FocusPanel’i kullanarak oluşturduk. Dikkat edecek olursanız, Şekil 2’de belirtmiş olduğum detaylara uygun olarak kaydırma çubuğunu 3 ana bölüme ayırdık. Ayrıca, kaydırma çubuğu alanı için de bir FocusPanel nesnesi kullandık, çünkü bu alanda da fare olaylarına sıkça ihtiyaç duyacağız. Kaydırma çubuğumuz varsayılan olarak gizli olacaktır, içerik alanınde herhangi bir taşma söz konusu olduğunda, Şekil 4’te belirtilen prensiplere uygun şekilde parametreleri hesaplayacağız ve kaydırma çubuğumuzu görünür hale getireceğiz (gerekirse tabii).

Gelelim prensiplere uygun olarak bu parametreleri nasıl hesaplanacağına…Bu işi de calculate() adlı bir metot ile yapacağız.

/**
 * Calculates the needed parameters of the scroll bar
 * Kaydırma çubuğu için gerekli olan parametreleri hesaplar
 */
private void calculate() {
	if (isInitialized()) {
		contentScrollHeight = content.getElement().getScrollHeight();
 
		windowHeight = window.getOffsetHeight();
		barHeight = windowHeight - 30;
		scrollHeight = barHeight - 56;
 
		int diff = contentScrollHeight - barHeight;
		if (contentScrollHeight > 0 && diff > 0) {
			// dynamic scroll bar ratio (of total height)
			// kaydırma çubuğunun boyutunu taşan içeriğin boyutunu göz önünde bulundurarak hesapla
			containerScaleRatio = (100 * scrollHeight) / contentScrollHeight;
			containerHeight = (scrollHeight * containerScaleRatio) / 100;
			space = scrollHeight - containerHeight;
 
			// content scrolling ratio
			// içerik kaydırma oranı
			contentScaleRatio = (100 * diff) / space;
 
			// set scroll height
			// kaydırma çubuğunun boyutlarını set et
			main.getElement().getStyle().setHeight(barHeight, Unit.PX);
			scroll.getElement().getStyle().setHeight(scrollHeight, Unit.PX);
			middle.getElement().getStyle().setHeight(containerHeight - 40, Unit.PX);
 
			setVisible(true);
			fireEvent(new ScrollBarVisibilityChangedEvent(true));
		} else
			setVisible(false);
	}
}

Kaydırma çubuğunun boyutunu ve kaydırma kademesini hesapladığımız metodumuz bu. Kısaca bu metodun nasıl çalıştığından da bahsedeyim. Burada hemen bir not düşmek istiyorum, bu metot içerisinde 30, 56, 40 gibi statik sayılar göreceksiniz, bu sayıların da aslında dinamik olarak HTML elemanlarının boşlukları (margin, padding vs.) göz önünde bulundurularak hesaplanması gerekiyor ancak ben bu iş için oturup vakit ayıramadım açıkçası, gerekli gördüğünüz durumlarda siz değişiklik yapabilirsiniz (bu kaydırma çubuğunu tiiiii geçen yaz -2011 yazı, aylardan Ağustos :)- programlamıştım, yeniden oturup düzenleme yapamadım, siz bu ufak tefek düzenlemeleri yapabilirsiniz diye tahmin ediyorum).

İlk olarak içeriğin, Şekil 3’te gösterdiğim ana konteynerdan taşıp taşmadığını kontrol ediyoruz. Eğer bir taşma söz konusu ise kaydırma çubuğuna ait diğer parametreleri hesaplayıp (kademe vs.) kaydırma çubuğunu görünür hale getireceğiz. Taşma olup olmadığını hesaplamak ise çok basit, ana konteyner penceresinin boyundan içerik alanının boyunu çıkardığımızda elde ettiğimiz sonuç sıfırdan büyükse taşma var demektir…

Eğer bir taşma varsa, taşmanın boyutuna göre, kaydırma çubuğunun boyutunu (ve otomatik olarak kaydırma alanı boşluğunu) hesaplıyoruz. Yukarıdaki kod içerisinde, IF kolu içerisindeki tüm işlemler bu değişkenleri hesaplamak için kullanılıyor, anlaşılması zor bir bölüm olmadığı için detayına girmeyip, işi yine size bırakıyorum burada…

Kod ile ilgili son olarak vurgulamak istediğim bir diğer nokta da, kaydırma alanı içerisindeki herhangi bir boşluğa tıkladığımızda, kaydırma çubuğunu ve içeriği hangi yönde hareket ettireceğimize nasıl karar vereceğimiz. Bu noktada izleyeceğimiz yol ise şu şekilde;

scroll.addClickHandler(new ClickHandler() {
	@Override
	public void onClick(ClickEvent event) {
		if (event.getY() < value)
			goTo(-containerHeight);
		else if (containerHeight + value < event.getY())
			goTo(containerHeight);
	}
});

Kaydırma çubuğumuzun pozisyonunu nesne içerisinde “value” değişkeni içerisinde saklıyoruz. Bu değer aslında kaydırma çubuğu HTML elemanının (bkz. Şekil 2 lütfen), kaydırma alanı içerisindeki, göreceli (relative) TOP değeri. Dolayısıyla, eğer kaydırma alanı içerisinde tıkladığımız yerin Y (dikey) konumu bu değerin altında ise, o zaman kullanıcı kaydırma çubuğunun üst (yukarı) bir bölümüne tıklamış demektir. Eğer kaydırma çubuğunun boyutu ile her konum değişiminde sakladığımız “value” değerinin toplamı, tıklanan yerin Y (dikey) konumundan küçükse, bu durumda kullanıcı kaydırma çubuğunun alt (aşağı) bir bölümüne tıklamıştır. Her iki durumda da kaydırma çubuğu, kaydırma çubuğunun boyu kadar ilerletilir.

Önemli gördüğüm kısımları elimden geldiği kadar detaylı anlatmaya çalıştım. Paylaşacağım kodlar içerisinde kaydırma çubuğunu çalıştımanız için gerekli olan tüm dosyaları (CSS, olaylar-events- ve olay yakalayıcılar-event handlers- vs.) bulacaksınız. Kodları incelediğinizde bütün herşey kafanızda yerli yerine oturacaktır.

Ve şimdi, programlanan kaydırma çubuğuna ilişkin çalışır durumda ekran görüntülerini paylaşmak istiyorum. İşte burada sizinle kodlarını paylaştığım, GWT ile programlanması tamamlanmış olan kaydırma çubuğumuz.

Şekil 5. Programlanması tamamlanmış bir kaydırma çubuğu (scrollbar)

Aşağıdaki ekran görüntüsünde ise, programlanan kaydırma çubuğunun bir web uygulaması içerisinde işlevsel olarak kullanıldığını görüyorsunuz. Tasarım açısından görsel bütünlüğün tam anlamıyla sağlandığını görüyorsunuz, tek amacımız da buydu zaten…

Şekil 6. Programlanması tamamlanmış ve bir web uygulaması içerisinde kullanılan kaydırma çubuğumuz

 

Happy coding! deyip bitirelim bu yazımızı da. Sağlıcakla…

, , , , , ,

13 Responses to GWT ile custom scrollbar programlama

  1. Sameeh 16 Mayıs 2012 at 10:48 PM #

    Hello,
    thanks for the component
    can you give an example how to attach the scollbars to the
    1- the main browser window
    3- a GWT panel (example SimplePanel or VerticalPanel)

    Thank you

    • Ahmet BÜTÜN 17 Mayıs 2012 at 10:15 AM #

      Hi Sameeh,

      There should be an example of what you need in source codes. Please check the source codes. If you cannot find the sample code in source codes, please let me know.

  2. Kk 22 Ekim 2012 at 5:06 AM #

    Hello ahmed,

    Can you please point to me where to download the source codd

    Thanks,
    K

  3. Ahmet BÜTÜN 26 Kasım 2012 at 11:14 AM #

    Hi Kk,

    I will add the new download link as soon as possible. Sorry for the broken link.

    thanks,
    Ahmet

  4. Saritha 27 Aralık 2012 at 9:31 AM #

    When i am adding a widget to the scrollbar then giving following exception,
    java.lang.IllegalStateException: SimplePanel can only contain one child widget
    at com.google.gwt.user.client.ui.SimplePanel.add(SimplePanel.java:67)
    at com.infor.ion.scrollbars.client.CustomScrollBars.onModuleLoad(CustomScrollBars.java:183)

    My code snippet is
    ScrollBar scrollBar = new ScrollBar();//Scrollbar class which you have written scrollBar.add(simplePanel);

    Can you please tell me how can i add a widget to it?

    • Ahmet BÜTÜN 27 Aralık 2012 at 10:05 AM #

      Hi Saritha,

      As you can see ScrollBar is a FocusPanel and a FocusPanel is actually a simple panel that makes its contents focusable, and adds the ability to catch mouse and keyboard events. Because of the fact that a simple panel can only contain one child widget you are getting this exception.

      You should change the source code of the scrollbar widget in order to add more than one widget. You can use the FlowPanel which is named “container” in the source code.

      thanks,

  5. Saritha 27 Aralık 2012 at 10:52 AM #

    Hi,
    Can you please give me the example of how to add this custom scrollbar to a panel(widget).
    When i am trying to add i am getting exceptions.I am unable to use it.
    Thanks,
    Sarita.

    • Ahmet BÜTÜN 27 Aralık 2012 at 2:10 PM #

      Hi Saritha,

      Can you please search the example source codes of the ScrollBar widget. It should have contain an example of how to use it.

      thanks

  6. Saritha 27 Aralık 2012 at 2:52 PM #

    Hi Ahmet,

    The rar which i downloaded, didn’t have such example. i have verified, but i am unable to find any such example.

    Can you please provide an example which tells, how to use the ScrollBar widget and how to handle the events in that widget.

    Thanks,
    Saritha

    • Ahmet BÜTÜN 28 Aralık 2012 at 10:41 AM #

      Hi Saritha,

      Here is an example, assume that you have flow panel named “main” and a container which is also a flow panel and you would like to attach a scrollbar to the “main” panel for the container panel area. In order to achieve this, you should do that,


      scrollBar.init(contentPanel, main.getParent().getElement());

      and that’s it. The key idea here is you must attach the scrollbar to the parent element of the main panel.

      i hope this will help you

Bir Cevap Yazın

Font Resize