30 Nisan 2021 Cuma

LiveCode Platformu ile Uygulama Geliştirmek

 Merhaba pandemi dönemi iş yok. Boş durma boşa çalış projesi olarak yeni neler öğrenebilirim diye internette geziyorum. GUI Builder olarak bilmediğim neler var diye bakarken LiveCode adında bir yazılım geliştirme platformu gördüm. Aslında ücretli bir yazılım ama https://livecode.org adresinde Open Source versiyonu da mevcut. Hobi için veya öğrenmek için bu kadarı yeterliymiş. İndirdim başladım dökümanlarını karıştırmaya. Birlikte karıştıralım güzel bir geliştirme ortamına benziyor.



LiveCode Uygulamasının Yapısı


LiveCode'da basit bir uygulama oluşturmak sadece birkaç dakika sürer. Önce bir stack (veya pencere) oluşturursunuz, ardından stack 'i butonlar , checkboxlar, text fieldler veya menüler gibi kontrollerle doldurursunuz. Son olarak, uygulamanıza nasıl davranması gerektiğini anlatmak için LiveCode’un İngilizce benzeri programlama dilini kullanırsınız.



Olay Güdümlü Programlama


Yaşasın bu da event driven programming çıktı. Bir LiveCode uygulaması, kullanıcı eylemleri tarafından yönlendirilir. LiveCode, bir butona tıklamak, bir alana yazmak, bir ağ üzerinden veri göndermek veya uygulamadan çıkmak gibi yaygın eylemler için bilgisayarı sürekli olarak izler.

Bir olay meydana geldiğinde, LiveCode bir mesaj gönderir. Başka platformlarda yok olay tetiklenir , yok olay işleyiciler var falan denir, bunlar da bu anlatımı seçmiş. Olay olunca evrene bir mesaj gönderir gibi mesaj gönderiliyor. Siz programınızı yazarken bu mesajların hangisine cevap vereceğinizi seçiyorsunuz. Örnek vermek gerekirse bir buton tıklandığında eğer o butona tıklanma cevabı veren bir program parçası tanımlamışsanız LiveCode o butona bir mesaj göndererek tıklamanın gerçekleştiğini bildirir. 

Olası olaylar çok fazla sayıda. Bir kullanıcı bir butonu tıkladığında, butona bir dizi olay gönderilir.  Örneğin, mouse buton görseli sınırları içinde ilk hareket ettiğinde bir mouseEnter mesajı gönderilir.  Ardından, mouse butonun üzerinde hareket ederken bir sürü mouseMove mesajı gönderilir. Mouse düğmesine basıldığında bir mouseDown mesajı gönderilir. Mouse düğmesi  bırakılınca  bir mouseUp mesajı gönderilir.

Bu mesajların her birine cevap vermek zorunda değiliz tabii ki, hangisine cevap vermek istersek oraya kod yazarak o olaya karşılık programımıza bir şeyler yaptırırız.



Nesne Temelli Programlama


Hah , event driven dan sonra Object Oriented da geldi. Demek ki harbi harbi GUI programlama ortamı. LiveCode kullanarak oluşturduğunuz herhangi bir uygulama nesneler ile yapılır. LiveCode ile genellikle herhangi bir kod yazmadan önce uygulamanızın nesnelerini oluşturursunuz. (her şey bir nesnedir derlerse de şaşırmam). Uygulamanızı oluşturan butonları, text field'ları ve diğer kontrolleri çizerek başlarsınız. Bunu yaparken diğer uygulama geliştirme platformlarındaki aynı şeyleri yaparsınız hemen hemen. Sürükle pencereye koy, boyutunu değiştir, hizala, öne getir-arkaya gönder, renk değiştir vs. 

Nesnelerinizi yerleştirdikten sonra programınızın çalışmasını istediğiniz şekilde olay mesajlarına cevap kodları yazmaya başlarsınız. LiveCode, her türlü kullanıcı arayüzünü oluşturmayı ve düzenlemeyi kolaylaştıran eksiksiz bir grafik geliştirme ortamı içerir. Butonlar , checkbox'lar, textfield'ler, menüler, grafikler ve çok daha fazlası dahil olmak üzere tüm temel işletim sistemi öğeleri için nesneler içerir.
Ek olarak, istediğiniz gibi görünen ve davranan kendi nesnelerinizi oluşturabilir ve özelleştirebilirsiniz.




Düzenleme ve Çalıştırma Modu



LiveCode platformunun en heyecan verici yanı. Diğerleri gibi programı yaz derle çalıştır ya da terminalden bir komutlar gir değil. Saniyesinde geliştirme ortamından çalıştırma ortamına geçiyorsunuz, aynı şekilde saniyesinde de düzenlemeye geri dönebiliyorsunuz. Bunu ilk gördüğümde yok daha neler demiştim. Süper özellik, alt tarafı bi "Hello World" yazacaz 50 tane dosya üreten bir derleme işinden geçiyoruz. Böyle olunca LiveCode hakikatten canlı canlı program yazıyorsunuz gibi oluyor.  

Çalıştırma modundayken, nesneler bir LiveCode uygulamasında mevcut olan tüm mesajları alır. Örneğin, çalıştırma modunda bir butona tıklamak ona bir mouseUp mesajının gönderilmesine neden olur, mouseUp mesajına yanıt verecek şekilde tasarladıysanız butonun kodu çalıştırılır.

Düzenleme modundayken, nesneler üzerlerine tıkladığınızda mesaj almaz ve nesnelerin özelliklerini değiştirebilir, onları taşıyabilir, yeniden boyutlandırabilirsiniz.

Tools Paletinde Düzenle(2) ve Çalıştır(1) modu arasında bir tıkla geçiş yapabilirsiniz.



Stack'ler ve Card'lar




Bir LiveCode uygulaması oluşturmanın ilk adımı bir pencere oluşturmaktır. Bildiğimiz pencerenin LiveCode adı stack olarak geçiyor. Bilinen frame yapısının biraz dışında bir şey bu. Bizim endüstriyel otomasyon projelerindeki HMI programları gibi. Bir uygulama penceresi var ama o pencerede göstermek için bir çok ekranlar olabiliyor. 

Her stack, card adı verilen bir veya daha fazla ekran içerir. Her card farklı bir görünüme sahip olabilir veya bir stack içinde tüm card'lar aynı görünebilir. Bir stack tek bir card veya birçok card içerebilir. Neyse ilerleyelim, örnek yaptıkça daha iyi anlaşılacak.

Bir uygulamada birden fazla stack tanımlanabilir. İlk tanımlanan stack Main Stack olarak değerlendirilir.  


Bir Stack Oluşturmak


File -> New Stack  seçerek yeni bir stack oluşturursunuz, ardından yeni stack boyutunu seçmenize olanak tanıyan bir alt menü gösterilir.




Stack'e Card eklemek


Object -> New Card seçerek pencereye yeni card eklenir. 




Card'a Nesneler Eklemek


Tools panelinden ekrana eklemek istediğimiz nesneyi sürükler istediğimiz pozisyona bırakırız.


Tabi yuvarlak içinde gösterilen Düzenleme Modu seçili değilken bunu yapamayız. 












LiveCode Mesaj İşleyişi


LiveCode olaylara dayalıdır. Bir scriptin gerçekleştirdiği her eylem, bir mesaj biçiminde gönderilen bir olay tarafından tetiklenir. 

Mesajlar olaylar tarafından gönderilir. Olaylar, kullanıcı eylemlerini (bir tuş ,yazma veya fare düğmesini tıklama gibi) ve program eylemlerini (bir dosya indirmeyi tamamlama veya uygulamadan çıkma gibi) içerir. LiveCode olayları izler ve bir olay meydana geldiğinde uygun nesneye bir mesaj gönderir.

Uygulamanızın belirli bir olay meydana geldiğinde bir şeyler yapmasını istiyorsanız, gönderilen mesajı, mesajla aynı ada sahip bir olay işleyici yazarak işlersiniz.


Mesajın Yolu



Mesaj yolu, hangi nesnelerin hangi sırayla bir mesaja yanıt verme fırsatına sahip olduğunu belirleyen kurallar kümesidir. Mesaj yolu, nesne hiyerarşisine dayanır. 

Her LiveCode nesnesi, farklı bir nesne türündeki başka bir nesnenin parçasıdır (nesne nesne içinde , nesne parent içinde , sen parentini bilmezsen bu nice programdır) (oruç kafaya vurdu). Örneğin, her kart bir yığının parçasıdır, gruplandırılmış her kontrol bir grubun parçasıdır ve bu böyle devam eder. Bu nesne hiyerarşisi, nesneler arasındaki sahiplik ve kalıtım ilişkisini tanımlar (ownership ve inheritance). 

Bir nesneye bir mesaj gönderildiğinde, genellikle o nesnedeki bir mesaj işleyicisi tarafından doğrudan işlenir. Bununla birlikte, hiçbir işleyici kod yoksa, mesaj, kendisine yanıt verebilecek bir olay işleyicisi bulana kadar bir yol boyunca devam eder. Nesne hiyerarşisi, bir mesajın gittiği yolla yakından ilgilidir. Çoğu durumda, bir nesne bir mesaj ilettiğinde, mesaj nesne hiyerarşisindeki nesnenin sahibine gider. Yani butonu tıkladığımızda olay işleyici çoğunlukla buton kodunun içinde olur.



Mesajın Yolunu İzleyelim


Hadi bir uygulamacık ile şu mesaj zımbırtısı nerelere gidiyor bakalım. Örneğin, kullanıcının ana stack içinde bir butonu tıklatarak LiveCode'un butona bir mouseUp mesajı göndermesine neden olduğunu varsayalım. Butonun kodu  mouseUp mesajı için bir işleyici içermiyorsa, mesaj butonun bulunduğu card'a iletilir. Card'ın kodu bir mouseUp işleyicisi içeriyorsa, işleyici çalıştırılır.
Ancak card mouseUp mesajını işlemezse, card'ın içinde olduğu stack'e mesaj aktarılır. Stack kodunda bir mouseUp işleyicisi içeriyorsa, işleyici çalıştırılır. Ancak stack , mouseUp mesajını işlemezse mesaj LiveCode Motor'a iletilir. Motor ne yapacak default bir işi varsa o mesajla onu yapacak. Sadece mouse tıklaması mesajı yok, mesela bir text field içinde bir tuşa basılmış olabilir bu durumda motora kadar mesaj gelirse yapacağı şey, basılan tuşa karşı gelen karakteri oraya eklemektir. Sonra da motor bu mesajı çöpe atar.


Örnek Uygulama


Şimdiye kadar yaptığımız bir şeyler varsa File -> Close and Remove From Memory seçerek kapatalım. Kaydetmek istersek bir isim verip kaydedebiliriz. 

File -> New Stack -> Default Size ile yeni pencereyi oluşturalım. Araç butonları menüsünden Inspector ile özellikleri açalım ve stack adını Mesaj Yolu olarak değiştirelim. 



Stack özelliklerine ulaşmak için Object -> Stack Inspector menüsü de kullanılabilir. Sonra ekrana iki buton ve bir Scrolling Field ekleyelim


Butonların Name özelliklerine Sil ve Mesaj Yolu yazıp enter basalım. Etiketleri otomatik olarak değişecektir. Field adını da log olarak girelim. 

Mesaj Yolu butonuna tıklanınca buton script'i log'a bir yazı yazarak kendisine mesaj geldiğini göstersin. Butonun scriptini açmak için üzerine sağ tıklayıp menüden Edit Script seçelim. Butona script eklememiz için script editörü açılacaktır.


Olayların listesinin olduğu bölümden mouseUp olayını bulup tıklayalım.



Buton tıklandığında, buton koduna bir mouseUp mesajı gönderilir, buton mesajı işliyorsa, bunu field içine yazar me mesaj orada kalır. Buton kodunu şöyle yazalım


on mouseUp pMouseButton
   put the name of me && "mouseUp mesajını aldı" & return after field "log"
end mouseUp

Bu kodu girdikten sonra Apply butonuna basıp kod etiketinin yanındaki sarı yuvarlağın içinin yeşil olması beklenir. Bunun anlamı yazılan kodda söz dizimi hatası yok demektir.


Kodu biraz irdelersek. Birinci ve üçüncü satır mouseUp olayını seçtiğimizde otomatik olarak eklendi. Bu kodu kendimiz de yazabilirdik. Anlamı mouseUp mesajı geldiğinde end mouseUp satırına kadar olan kodu çalıştır. Arada sadece bir tek satırlık kod var. the name of me şuanda kodunun içinde bulunulan nesneyi ifade ediyor. Yani burada butonun kodunun içinde olduğumuza göre buton nesnesinin adı yazılacak. & işlemi ile iki yazı birleştiriliyor (Visual basic gibi). && kullanılmasının nedeni iki yazıyı birleştirirken arada bir boşluk olsun, birbirlerine yapışmasınlar diye. Yazılacak satırın sonuna bir de return ismi ekleniyor ki bu da alt satıra geçsin diye eklenmiş. after field "log" demek log field'ine sona ekle demek (diğer birçok dildeki append gibi). Mesela orada into field "log" yazsaydık field içindeki tüm yazının yerine bu satırı yazacaktı. Neyse ilerledikçe kodlama hakkında öğreneceklerimiz çok fazla. 

Şimdi çalıştırma moduna geçip butona tıklarsak 


Her basışımızda bir satır daha ekleniyor. Yani buton kodumuza kadar mesaj ulaşıyor. Sil butonuna da bir kod yazalım da ortalığı boşaltsın.

on mouseUp pMouseButton
   put "" into field "log"
end mouseUp

Bir şey daha var buton kodundaki pMouseButton parametresi ile mouse'un hangi butonunun tıklandığı bilgisi gelir. Sol tuş 1, orta 2, ve sağ tuş 3 gibi. 

Buton için yazdığımız scripti aynen card için de yazarsak (kopyalayıp yapıştırabilirsiniz), bakalım o mesajı ne zaman alacak? Card üzerinde sağ tıklayıp Edit Script seçerek girelim. Şimdi butona tıkladığımızda değişen bir şey olmadığını sadece buton kodundan cevap geldiğini görürüz. Çünkü buton kodu işledi mesaj orada bitti. Mesajın yoluna devam etmesi için buton kodunu şöyle değiştiririz. 

on mouseUp pMouseButton
   put the name of me && "mouseUp mesajını aldı" & return after field "log"
   pass mouseUp
end mouseUp

pass mouseUp satırı bir sonraki istasyona mouseUp mesajı gönderiyor. Şimdi butona tıklarsak


Mesaj yolu şeması ne diyordu? Grup içindeki nesneye, aradan gruba, oradan card'a, oradan stack'e , oradan da motora. Görmek için butonu grup içine alalım. Grup içine alınacak nesneler seçilir ve toolbox'ta group simgesi tıklanır.  Biz sadece Mesaj Yolu butonunu tek başına grup içine alalım. Inspector'de gruba isim olarak Buton Grup girelim. Show Name ve Show Border seçeneklerini de aktif ederek grup çigilerinin ve adının görünmesini de sağlayalım.


Properties panelinden direk grubun koduna Edit Script sembolünü tıklayarak geçebiliriz.


Butona yazdığımız aynı kodu buraya da kopyalayalım. Dikkat edersek olay seçenekleri içinde mouseUp yok ama biz koda butondaki aynı kodu yazarsak grup da mouseUp mesajına cevap verecektir.


Şimdi çalıştırma moduna geçip butonu tıklarsak


Sırada stack kodu var. Object -> Stack Script menüsünden stack koduna ulaşırız. Oraya da aynı şeyleri yazalım


 Artık mesajımızı sırayla kimler okuyor görüyoruz. Buradan sonrası motora gider.


Kod editöründe gıcık birşey var. Enter basınca alt satıra geçmiyor, Apply butonu basılmış gibi kodu kaydedip editörden çıkıyor. Alt satır için Shift+Enter basmak gerekiyor.








Bir Stack'e Nesneler Eklemek


Bir LiveCode uygulaması oluşturmanın ilk adımı, grafik kullanıcı arabirimini (GUI) oluşturmaktır. LiveCode ile genellikle herhangi bir kod yazmadan önce uygulamanızın nesnelerini oluşturursunuz.

Nesneleri yerleştirdikten sonra, gereken her nesneye kod ekleyerek devam edebilirsiniz.



Yeni Bir Stack Oluşturun


Uygulamanızı oluşturmanın ilk adımı, File menüsünden New Stack seçerek yeni bir stack oluşturmaktır. Varsayılan boyutu seçebilir veya mobil cihazınıza uygun bir boyut seçebilirsiniz. 

Ardından, araç çubuğundaki Inspector tıklayarak Stack Özelliklerini açabilir ve stack name olarak "İlk Uygulamam" adını veya seçtiğiniz herhangi bir adı verebilirsiniz.



Stack'e Nesneler Eklemek




Şimdi stack'e nesneler eklemek istiyorsunuz. Bunu, Tools Paletinden istediğiniz nesneleri stack'inize sürükleyerek yaparsınız.

Birkaç nesne eklemeyi deneyin.




Nesneleri Hizalama




Gerçekten güzel görünen bir arayüz elde etmek için nesnelerinizin birbiriyle doğru şekilde hizalandığından emin olmak istersiniz.

Yığınınıza bir kaç field ekleyin, onları biraz dağıtın.



Nesneleri Hizalama - Seçenek 1




Field'ları dikey olarak sıralamanın birkaç yolu vardır.

İlk yol, Property Inspector panelini kullanarak her Field konumunu belirlemektir:
  1. Düzenleme modunu seçin
  2. Field'i seçin
  3. Property Inspector açın
  4. Size and Postion sekmesine gelin
  5. Left özelliğini 20'ye veya olmasını istediğiniz herhangi bir değere ayarlayın
Sonra bunu tüm Field'ler için tekrarlayın.



Nesneleri Hizalama - Seçenek 2




Diğer bir seçenek, tüm Field'ları seçmek ve Property Inspector kullanarak bunları hizalamaktır.
  1. Düzenleme moduna geçin
  2. İlk Field'i seçin ve yerine götürün
  3. Mouse ile tüm Field'leri seçin. Mouse'u sürüklerken ilk seçilen eleman az önce pozisyonunu belirlediğimiz eleman olmalıdır.
  4. Property Inspector açın
  5. Align Controls sekmesine geçin - bu seçenek yalnızca birden çok nesne seçildiğinde kullanılabilir. Görmek için Property Inspector'deki sekmelerin sağındaki >> butonuna tıklamanız gerekebilir. Bu buton olası bütün sekmeler görüntüye sığmazsa görünür.
  6. Align bölümündeki Align Objects Left butonuna tıklayın
Elemanları mouse ile seçerken ilk seçtiğiniz elemanın sol kenarına diğer hepsi hizalanacaktır. 

Not: Ctrl tuşunu basılı tutup birden çok elemanı tek tek tıklayarak veya "Düzenle" modundayken üzerlerine sürükleyerek birden çok nesne seçebilirsiniz.


Align Controls Sekmesindeki Diğer Hizalamalar



Align Controls sekmesinde başka bazı seçenekler de var
  1. Nesnelerin genişliklerini ve / veya yüksekliğini eşit yapın
  2. Nesneleri yatay veya dikey olarak hizala
  3. Nesneleri eşit olarak boşluk bırak (yatay veya dikey)
  4. Nesneleri her seferinde bir piksel sürükleyin (ince ayar)
  5. Kontrolün bulunduğu katmanını değiştirin

Not: Bir elemanın katmanı, card üzerindeki sırasıdır (arkadan öne doğru). İki eleman üst üste gelirse, diğerini örten daha yüksek bir katmana sahip olur. Her kontrol farklı bir katmandadır.







Bir Stack İçinde Gezinme


Oluşturduğunuz çoğu stack ve uygulamanın birden fazla card'ı vardır, peki stack'inizdeki card'lar arasında nasıl gezinirsiniz?


Yeni Bir Stack Oluşturun




File menüsünden yeni bir stack oluşturun. Property Inspector açın ve Name alanında (1) stack adını Navigasyon  olarak girin.


Stack'e Card Eklemek



Object -> New Card menüsü ile stack'imize bir card ekleyelim. Bunu iki defa yapalım toplamda 3 adet card'ımız olsun. 

Dikkat ederseniz şimdi pencere title bar'da Navigasyon (3) yazıyor. Bu Navigasyon penceresi 3 nolu ekranda olduğumuzu belirtiyor. Yani Stack içinde 3.üncü  Card



IDE'de Gezinmek - View Menüsünü Kullanmak




Geliştirme sırasında, View menüsünü kullanarak Card'lar arasında geçiş yapabilirsiniz. 

Opsiyonlar sırasıyla şöyle: İlk ekrana git, sıralamada bir önceki ekrana git, sıralamada bir sonraki ekrana git, en sondaki ekrana git ve bir önce gitmiş olduğun ekrana yani buraya gelmeden önce bulunduğun ekrana git.

 Deneyin, hangi kartta olduğunuzu size göstermek için stack başlık çubuğunda gösterilen sayının değiştiğine dikkat edin (Şu anda ekranlarda bir şey olmadığı için) . Dolayısıyla, Go First seçerseniz, ilk kartta olacağınız için numara 1'e değişecektir.




IDE'de Gezinme - Proje Tarayıcısını Kullanma



Project Browser kullanarak card'lar arasında da hareket edebilirsiniz. Project Browser, tüm açık stack'lerin, her stack'deki card'ların ve her card'daki kontrollerin bir listesini içerir.

Tools -> Project Browser ile açılır. 

Navigasyon'unn (1) yanındaki + simgesine tıklayarak görünümünüzü genişletin

Project Browser'da (2) çift tıklayarak bir card'a gidebilirsiniz.

Ayrıca herhangi bir nesnenin kodunu da düzenleyebilirsiniz (3).




Nesneleri ve Kodları Kullanarak Gezinmek


Uygulamanızı ister masaüstü ister mobil olsun bağımsız bir şekilde oluşturduğunuzda, kullanıcılarınız yukarıda açıklandığı gibi ekranlarda gezinemezler, bu nedenle ekranlar arasında hareket etmeleri için yollar sağlamanız gerekir.

Bunu, uygulamaya nesneler ekleyerek ve geçerli ekranı değiştirecek nesnelere LiveCode scriptleri ekleyerek yapabilirsiniz.

Başlamadan önce sayfalara isim verelim ve hepsine birer başlık yazısı ekleyerek hangi sayfada olduğumuzu daha net görelim.

Sayfalara sırayla Sayfa 1 , Sayfa 2 ve Sayfa 3 isimlerini Name özelliklerine yazalım. Label özellikleri şöyle:

Contents : Sayfa 1 (veya 2 veya 3)
Text Size : 36
Width : Fit Content
Height : Fit Content
Left : 135
Top : 0

Çalıştırma moduna alıp Ctrl+2 ya da Ctrl+3 basarak ekranlar arasında gezersek başlıkları tek tek görebiliriz.



Gezinme Butonları Ekleme


Sayfanın birine 2 tane buton ekleyelim isimleri İleri ve Geri olsun. 


Hizalamak için butonları seçip önce Align Objects Bottom ile aynı yatay hizada olmalarını sağlayıp. Arkasından yatay Distribute seçeneklerinden Across Card seçerek yatayda ekran içinde eşit aralıklarla dağıtım sağlanabilir.

Kod editörünü açalım ve İleri butonu mouseUp koduna şunu yazalım.
on mouseUp pMouseButton
   go to the next card
end mouseUp


Geri butonunun koduna da şunu yazalım
on mouseUp pMouseButton
   go to the previous card
end mouseUp

Bu butonları kopyalayıp diğer sayfalara da yapıştıralım ve test edelim.




Diğer Gezinme Seçenekleri


Ekranları isimleri ile çağırmak için sayfaya 3 buton ekleyelim mesela


Sayfa 1 butonu kodu:
on mouseUp pButtonNumber
   go to card "Sayfa 1"
end mouseUp

Diğerleri de benzer şekilde girip deneyelim.

Kaçıncı Card olduğuna göre de gitmek için 3 buton ekleyelim


Mesela 3. Card butonunun kodu:
on mouseUp pButtonNumber
   go to card 3
end mouseUp

Inspector panelinde gördüğümüz id numarası ile card'a ulaşmak için:


3 buton ekleyelim



id 1004 butonu için kod:
on mouseUp pButtonNumber
   go to card id "1004"
end mouseUp









LiveCode'da Özellikler Kullanımı


Bir özellik, LiveCode nesnesinin bir niteliğidir. Her nesne türünün, nesnenin görünümünü veya davranışını etkileyen birçok yerleşik özelliği vardır. Ayrıca herhangi bir nesne için ısmarlama özellikler tanımlayabilir ve bunları her türlü veriyi depolamak için kullanabilirsiniz.



Yeni Uygulama




Yeni bir stack oluşturun, 'Özellikler' olarak adlandırın ve bir Scrolling Field ve bir Button ekleyin. Alanın çoğunu dolduracak şekilde Field'ı büyütün.

Field seçiliyken Toolbar'daki Inspector butonu tıklayarak ya da elemana sağ tıklayıp Property Inspector seçerek eleman özelliklerini açalım.


Bu panelde bir çok sekme var. Şu anda temel özelliklerin olduğu sekmedeyiz. Sekmeler sembollerle ifade edilmiş ama ille de yazılı etiket istersek sağ üstteki ayarlar sembolüne tıklayıp açılan menüde Tab Display Style olarak Label seçeriz.


Özellikler içinde biraz gezinip bakın. Hangileri ne işlere yarıyor tecrübe edin.



Özellikleri Kod ile Değiştirmek


Tüm özellikler aynı zamanda script ile de ayarlanabilir. Örneğin:
set the visible of field 1 to true

1.inci field elemanının visible özelliğini true yap demek field elemanının özelliklerinde Visible değerini seçili yapmak ile aynı işi yapacaktır. 

Property Inspector'da açıklamasının üzerine gelerek bir özelliğin kod içinde nasıl yazılacağını gösterilen tooltipte görebilirsiniz. Tercih ederseniz, Property Inspector'u açıklamalar yerine özellik adlarını görüntüleyecek şekilde ayarlayabilirsiniz.



Başka bir yol da Edit -> Preferences ​​açın ve General altından şunu seçin:

Property labels are: Name of LiveCode property




Bir Özelliğin Değerini Okumak


get veya put komutlarını kullanarak bir özelliğin değerini alabilirsiniz. Değeri bir değişkene koyabilir veya doğrudan kullanabilirsiniz.

Bir özellik değerini elde etmek:
get the visible of field 1

Bir özellik değerini bir değişkene koymak:
put the visible of field 1 into tVisible

Bir özellik değerini doğrudan kullanmak:
if the visible of field 1 then
	...
end if




Özellik Kalıtımsallığı


Çoğu özellik, parçası oldukları nesneye özgüdür ve yalnızca o nesneyi etkiler. Bununla birlikte, bir nesnenin rengi ve metin yazı tipi gibi bazı özellikleri, nesne hiyerarşisinde üstündeki nesnenin ayarlarını devralır.

Örneğin, bir field ön plan (metin) renk özelliği belirtilmezse, field sahibi olan card'ın ön plan rengini alır. Card için de ön plan rengi belirtilmezse, stack ön plan rengi kullanılır ve bu böyle devam eder.

Bu, bir stack için ForegroundColor değeri ayarlayınca içindeki her nesnenin eğer kendilerine özel olarak ForegroundColor tanımlanmadıysa stack için tanımlanan rengi kullandığı anlamına gelir.

Bu önce nesneyi, sonra nesnenin sahibini, sonra bu nesneye sahip olan nesneyi vb. kontrol etme ve özelliği onlardan alma işlemine özelliklerin kalıtımı denir. Her nesne, hiyerarşide üstündeki nesnenin metin rengini devralır.

BackgroundColor, topColor, bottomColor, borderColor, shadowColor ve focusColor özellikleri, bunlara karşılık gelen desen özellikleri ve textFont, textSize ve textStyle özellikleri için benzer kalıtım kuralları geçerlidir.




Kullanıcı Tanımlı Özellik


Kullanıcı tanımlı özellik, standart özelliklerine ek olarak bir nesne için oluşturduğunuz bir özelliktir. Herhangi bir nesne için kendi özelliklerimizi tanımlayabilir ve bunları her türlü veriyi depolamak için kullanabilirsiniz. Bir nesne için istediğiniz kadar kullanıcı tanımlı özellik oluşturabilirsiniz.

Şunları yapmak istediğinizde kendi özelliğinizi kullanın:
  • verileri belirli bir nesneyle ilişkilendirmek
  • verileri nesne ile stack dosyasında kaydetmek
  • verilere çok hızlı erişim



Property Inspector ile Kendi Özelliklerinizi Üretmek



Property Inspector veya script ile kendi özelliklerinizi oluşturabilirsiniz.

Property Inspector'de yeni bir özellik oluşturmak için nesneyi seçin ve Property Inspector açın, Custom sekmesine gelin. Set açılır menüsünden (1) customKeys seçin (default bu seçili olmalı).

Ardından, yeni bir özellik oluşturmak için Add new element butonuna tıklayın, onu adlandırın (2) ve ardından değeri (3) ayarlayın.




Script ile Kendi Özelliğinizi Oluşturun


Yeni özelliği bir değere ayarlayarak script içinde kendi özellik eklemenizi yapabilirsiniz. Var olmayan bir özellik ayarlarsanız, LiveCode otomatik olarak kullanıcı tanımlı özelliği oluşturur ve istenen değere ayarlar.
set the cEnÇokSatır of field 1 to 20

Bu kod field 1 elemanına cEnÇokSatır adında kendi özelliğimizi eklememizi sağlar. Yeri gelmişken küçük kod parçalarını denemek için ToolBox üzerindeki Message Box'ı açıp orada kodu çalıştırıp sonucu görebilirsiniz.






Kullanıcı Tanımlı Özellik Değerini Okumak ve Değiştirmek


Aynı diğer özellikler gibi kendi tanımladığımız özellikleri okur ve yazarız:

Ismarlama özelliğin değerini ayarlama:
set the cMaximumLines of field 1 to 50

Ismarlama özelliğin değerini okuma:
get the cMaximumLines of field 1








Buton Scriptleri


Bir LiveCode uygulaması, kullanıcı eylemleri tarafından yönlendirilir. Kullanıcı, bir butona tıklayarak veya bir field'ayazarak uygulamanızla etkileşime girdiğinde, LiveCode bir mesaj gönderir.

Programınızın hangi mesajlara yanıt vermesini istediğinize karar verir ve bu mesajları işleyen kodları eklersiniz.

Burada butonlar tarafından gönderilen bazı mesajlar için kod örnekleri yapacağız.


Yeni Uygulama



Yeni bir stack oluşturun, Buton Mesajları (1) olarak adlandırın ve stack içine bir scrolling field (2) ve bir button (3) ekleyin.

Field elemanını çift tıklayarak Inspector açın ve adını Yazı olarak değiştirin. Sonra butonu tıklayın Inspector buton özelliklerini göstermeye başlar. Butonu da Kırmızı olarak adlandırın (4).



Field Elemanına Yazı Ekleyin



Field'a biraz metin ekleyin, bu örnekte bir miktar Lorem Ipsum metni kullandım, ancak herhangi bir metin uygun olacaktır. https://www.lipsum.com adresinde buna benzer test yazıları üretebilirsiniz. Metni iki şekilde ekleyebiliriz ya Inspector'de Contents sekmesine gider orada ekleriz ya da çalıştırma moduna geçip metni ekleyip tekrar düzenleme moduna döneriz.

Not: Özellikle çalıştırma modunda metni  kopyalıyor / yapıştırıyorsanız, hiçbir biçimlendirme uygulanmamış düz metin olduğundan emin olmalısınız. Bunu örneğin önce bir metin düzenleyiciye yapıştırıp düz metne ayarlayarak veya LiveCode menüsünde Edit -> Paste Unformatted seçeneğini kullanarak yapabilirsiniz. Kullandığımız field içine biçimlendirme uygulanmış metinler de alabilir. Eğer öyleyse az sonra metin rengini değiştirirken sorun çıkabilir.




Butonun Kodu


Butona sağ tıklayıp Edit Script seçerek ya da nasıl isterseniz öyle butonun kod penceresini açalım. 

Butonlara gönderilen bazı mouse mesajları:

mouseEnter: Mouse işaretçisi bir nesneye girdiğinde gönderilir

mouseMove: kullanıcı mouse'u nesne alanı içinde hareket ettirdikçe sürekli gönderilir

mouseDown: kullanıcı mouse'un herhangi bir düğmesine bastığında gönderilir

mouseUp: kullanıcı mouse düğmesini bıraktığında gönderilir

Bu durumda, mouseUp'ta bir şey olmasını istiyoruz. Bu standarttır kullanıcı buton üzerinde tıklar ve sonra da bırakırsa işlem yapılır. Kullandığımız programların geneline bakarsak mouse düğmesine bastığımızda değil bıraktığımızda işlem yapılır. Hatta yanlış düğmeye basmışsak düğmeden parmağımızı kaldırmadan boş bir yere gider orada bırakırız. Örnek olarak field metin rengini değiştireceğiz, böylece kodu şu şekilde ayarlayacağız:
on mouseUp pMouseButton
   set the textColor of field "Yazı" to red
end mouseUp

Koddaki textColor özellik adı aslında Inspector'de field özelliklerine bakınca karşımıza çıkmıyor. Doğrusu foregroundColor olacak. Gerçi bu da çalışıyor, demek ki LiveCode anlıyor. 
on mouseUp pMouseButton
   set the foregroundColor of field "Yazı" to red
end mouseUp

Butonu deneyelim,






mouseDown İşleyicisi Ekleme




Bir buton kodunda birden fazla mesajı işlenebilir, butonun kodunu açalım ve field metin rengini maviye çeviren bir mouseDown işleyicisi ekleyelim. 
on mouseDown pButtonNumber
   set the foregroundColor of field "Yazı" to blue
end mouseDown


Artık 'Çalıştır' moduna geçip butonu tıkladığınızda, buton basıldığında metin maviye, buton bırakıldığında kırmızıya döner.




Mesaj Yoluınu Kullanmak



Burada Mesaj Yolu'nu kullanarak Card için yazacağımız tek bir script ile 3 butonun tıklanmasını yakalayacağız. Buton kodlarımızın hepsini boşaltalım. Renk bilgisini saklamak için butonların Name özelliğini kullanacağız. Bu yüzden butonun Name ve Label özelliklerini bu sefer farklı ayarlıyoruz.


Diğer butonlar için değerler green - Yeşil ve red - Kırmızı olacaktır. 



Card'a mouseUp Mesajı Kodu Yazalım


Object menüsünden Card Script seçerek script düzenleyicisini açın. Alternatif olarak, card üzerinde sağ tıklayıp menüden Edit Script seçebilirsiniz.

Card scriptine bir mouseUp işleyicisi ekleyin, bunun içinde hangi butona basıldığını kontrol etmek için target işlevini kullanırız.
on mouseUp pButtonNumber
   local tColor
   
   ## target fonksiyonu ile hangi butona basıldığı alınır
   ## the short name of the target ile blue green ve 
   ## red değeri gelir bunlarla renk değiştirebiliriz
   put the short name of the target into tColor
   set the foregroundColor of field "Yazı" to tColor
end mouseUp


Deneme yaptığımızda hangi butona tıklarsak onun Name değerindeki renge göre yazı renginin değiştiğini görürüz. Tabii butona değil de card üzerinde başka bir yere basarsak:


Tıkladığımız elemandan aldığı Name özelliği değeri bir renk ismi olmayınca script orada bir hata verdi ve program işleyişini durdurarak debugger ekranını açtı. Burada hata mesajını anladıktan sonra Stop butonu ile script işleyişini iptal edebilir ya da diğer butonlarla adım adım falan çalıştırabiliriz. 

Ne yapmak gerekiyormuş? Nereye tıklandı , butona mı diyerek ya da gelen isim değeri bir renge karşılık geliyor mu? diye kontrol etmek gerekiyormuş. Ama biz burada işin temellerindeyiz, inşallah daha sonra bunlara da bakarız. 



Kendi Fonksiyonumuzu Kullanmak


3 buton için de benzer kodları tekrar tekrar yazmayalım diye bir fonksiyon tanımlayıp onu da kullanabiliriz. Butonlardan bu fonksiyonu parametresinde ilgili renk olacak şekilde çağırabiliriz. 

Kendi komutlarınızı ve fonksiyonlarınızı diğer herhangi bir komut veya fonksiyonla aynı şekilde kullanırsınız. Sadece kendi komutunuzun adını yazarak  çalıştırabilirsiniz.

Özel fonksiyonumuz çağrıldığında, fonksiyonla aynı ada sahip bir mesaj gönderilir. Fonksiyon adıyla bir mesaj işleyici yazarak fonksiyonumuzun mesajına yanıt veririz. Bir nesne belirtmezsek, mesaj, kod çalıştırılan nesneye gönderilir ve ardından mesaj hiyerarşisini normal şekilde iletir.

Tanımlı komutlar gibi, bizim tanımladıklarımız da LiveCode'a bir şeyler yapması için verilen bir talimattır. Tanımladığımız fonksiyonların parametreleri fonksiyon isminden sonra girerek ifade ederiz.




yazıRengi Fonksiyonu Tanımlayalım


Card Scriptini açın ve aşağıdaki fonksiyonu ekleyin (tabi daha önce yazdığımız kodları silin).
command yazıRengi pColor
   set the foregroundColor of field "Yazı" to pColor
end yazıRengi

Bu komut tanım pColor parametresini alır ve field rengini o parametrenin değerine ayarlar. Mesaj panelini açıp değişik renkler deneyebilirsiniz.

Şimdi Kırmızı butonu scriptini açıp, mouseUp işleyicisine kendi komutumuzu çağıran kod yazalım.
on mouseUp pMouseButton
   yazıRengi red
end mouseUp

Diğer butonlara da scriptlerini kendi renklerine göre yazıp çalıştırabiliriz. Tabi tek satırlık iş için kendi komutumuzu yazmaya gerek yok ama yapılacak yüzlerce satırlık iş olursa üç butona tek kod yazmak mantıklı olacaktır.



Komutumuzu Card Scriptinden Çağırmak


Butonlardaki kodu silip card scriptine şunları da yazabiliriz:
on mouseUp pButtonNumber
   put the short name of the target into tColor
   yazıRengi tColor
end mouseUp









LiveCode İçinde Değişkenler


Değişken kavramı sadece LiveCode ile sınırlı değildir, aynı zamanda bugün mevcut olan tüm programlama dillerinde görülebilir. Basitçe ifade etmek gerekirse, değişken, uygulamanızda oluşturduğunuz verileri depolamak için bir yerdir. Elinizin altında birçok özel / gelişmiş değişken türü vardır (örneğin, array, parametreler gib), ancak bu ders bağlamında değişkenlerin 3 ana türüne (kapsamına) odaklanılmış. Bunlar yerel (geçici), script yereli ve global değişkenler.



Değişkenlerin Görselleştirilmesi



İlk olarak, bir değişkenin basit bir görselleştirmesiyle başlayalım. Yukarıda (1), Bob evine girmek istiyor. Gerçek dünyada, kapıyı açar ve (2) içeriye girerdi.

LiveCode'da süreç çok benzer ... eve "bob"u koymak istiyoruz. Bunu tasvir eden gerçek bir LiveCode betiği:
put "bob" into tHouse

Bu örnekte, bob bizim verimiz ve tHouse bizim değişkenimizdir. 



Değişken Adlandırma


Yukarıdaki örneklerde tHouse değişkeninin başında küçük bir "t" harfini kullanılmış olduğunu fark edebilirsiniz. Bunun gibi harfler kullanılır, böylece hangi değişken kapsamıyla uğraştığımızı kolayca biliriz. "t" kullandığımızda bu geçici bir değişkendir, "s" scrip yerelidir ve "g" globaldir.

Karakter Örnek Kapsam
g gVar Global değişken
t tVar Olay işleyicide değişken 
(geçici - temporary)
s sVar Script lokal değişken
p pVar Komut parametresi
k kVar Sabitler
c cVar Custom özellik
kullanıcı tanımlı


Bunlar, burada LiveCode HQ'da kullanılan adlandırma kurallarıymış, ancak herkes istediği notasyonu kullanabilir.. Şimdi bu değişken türlerinin her biri arasındaki farklara bakalım.



Geçici Değişkenler


Bu tür değişkenler, verilerini yalnızca onları çağıran işleyicide tutar. Bir işleyiciyi birden fazla çalıştırırsanız, işleyici her çalıştırıldığında değişken sıfırlanır. Bunun bir örneği, bir buton komut dosyası içinde aşağıdakine benzer bir şeydir:
on mouseUp
   local tToplam
   put 1 into tToplam -- tToplam bizim geçici değişkenimiz
   repeat with x = 1 to 10
      add 1 to tToplam -- 10 defa tToplama 1 ekle
   end repeat
   put tToplam -- bu tToplam değerini mesaj kutusuna yazar
end mouseUp

Bu kodu her çalıştırdığınızda, kaç kez çalıştırırsanız çalıştırın mesaj kutusu 11'i gösterecektir. Bu, bu değişkenin geçici doğasından kaynaklanmaktadır. İşleyici kodun başında local kelimesiyle oluşturulur,  işleyici biter bitmez yok olur. 

Katı Derleme Modunda (yani mesela Windows için .exe dosya oluştururken), geçici değişkenler kullanılmadan önce bildirilmelidir. Bu durumda, local komutunu kullanmak gereklidir ve bu genellikle işleyicinin en üstündedir.




Script Local


Script yerel değişkenleri, geçici değişkenlerden bir adım yukarıdadır, çünkü yalnızca değerlerini korumakla kalmaz, bir nesne script'i içindeki herhangi bir işleyici bu değerleri kullanabilir. Bu amaç için yukarıdaki örneği ayarlayalım:
local sToplam

on mouseUp
   repeat with x = 1 to 10
      add 1 to sToplam -- 10 defa sToplam'a 1 ekle
   end repeat
   put sToplam -- bu sToplam değerini mesaj kutusuna yazar
end mouseUp

on mouseDown
   if sToplam is empty then
      put 1 into sToplam
   end if -- Eğer sToplam değer verilmediyse
   repeat with x = 1 to 10
      add 1 to sToplam -- 10 defa sToplam'a 1 ekle
   end repeat
   put sToplam -- bu sToplam değerini mesaj kutusuna yazar
end mouseDown


O buton orada durduğu müddetçe buton scriptine bağladığımız sToplam değişkeni en son verdiğimiz değeri koruyacaktır. Tabi ki buton scripti içinde kullanılırken. 

Geçici değişkenlerin aksine, komut dosyası yerelleri, Sıkı Derleme Modu dışında bile kullanılmadan önce her zaman bildirilmelidir. Bu tür bir değişken için local komutunu kullanırız ve bu genellikle nesnenizin script dosyasının en üstündedir.

Bildirim orada olmasaydı, Katı Derleme Modunda olmadığında değişken basitçe geçici bir değişken olarak değerlendirilirdi.



Global Değişkenler


Son standart değişken tipimiz Global değişkendir. Bunları benzersiz kılan, uygulamanızın herhangi bir yerinden erişilebilmeleridir (beyan edildikleri sürece). Aşağıdaki örnekte, globaller için düzenlenen repeat döngüleri 2 değişik buton içinde kullanılmıştır.


Button 1

global gToplam

on mouseUp
   repeat with x = 1 to 10
      add 1 to gToplam -- 10 defa gToplam'a 1 ekle
   end repeat
   put gToplam -- mesaj kutusuna değeri yaz
end mouseUp


Button 2

global gToplam

on mouseUp
   put "gToplam'ın değeri " && gToplam
end mouseUp


Daha önce olduğu gibi, repeat döngümüz 10 kez değişkenimize 1 ekliyor. Değişkenimiz artık global olduğundan, bunu global komutuyla ilan etmeliyiz.

Bu değişkeni erişmek istediğiniz her yerde bildirmeniz gerekecek ve her 2 butonda da bunu yaptık. Artık değişken değerini 1. butondan ayarlıyoruz ve 2. butondan da bu değere kolayca ulaşabiliyoruz.

Global değişkenlerin kullanımı programlar çok büyüdükçe isim karıştırmalarıyla falan başımızı ağrıtabilir. Alternatif yöntemler üzerine LiveCode takımı bir ders yazmış:









Kullanıcıyla İletişim Kurmak


Sıklıkla kullanıcınızla iletişim kurmak, onlara sorular sormak, bir klasör veya dosya seçmelerini istemek ya da onlara seçenekler sunmak veya bilgi vermek isteyeceksiniz.

LiveCode, bu tür bir iletişime izin vermek için bir dizi ön tanımlı iletişim kutusu sağlar, bu iletişim kutuları tüm platformlarda plaform standartlarına göre görüntülenir.



Answer Diyaloğu



Answer diyaloğu, ekranda bilgi görüntülemenize olanak tanır ve isteğe bağlı olarak kullanıcının yedi seçime kadar bir listeden seçim yapmasına olanak tanır. Hiçbir seçim belirtilmezse, bir OK butonu gösterilir.

answer komutu, metin ve buton seçimlerini belirlemenize olanak tanır ve tıklanan düğmeyi geri döner. Pencere başlığının yanı sıra görüntülenecek bir simgeyi de belirtebilirsiniz.

Yazı tipi, nesne konumları, düğme sırası ve simge, işletim sistemini yansıtacak şekilde otomatik olarak değişir.

Yukarıdaki örnek resmin Windows siteminde butondaki kodu şöyle:
on mouseUp pMouseButton
   answer question "Devam edecek misiniz?" with "Bilmiyorum" or "Hayır" or "Evet"
end mouseUp




Ask Diyaloğu




Aşk değil yanlış anlaşılmasın. İngilizce Ask (sormak). Ask diyaloğu, kullanıcıya bir soru sormanıza ve  cevabını yazabilecekleri bir yer sunmanıza olanak tanır. 

Ask komutu soruyu, pencere başlığını ve simgeyi belirlemenizi sağlar. Answer diyaloğunda olduğu gibi yazı tipi, nesne konumları, buton sırası ve simge otomatik olarak işletim sistemini yansıtacak şekilde değişir.

Ask diyaloğu OK ve Cancel butonlariyla gösterilir. Kullanıcı OK tıklarsa, metin kutusunun içeriği it değişkenine yerleştirilir ve result boş döner. Kullanıcı iletişim kutusunu iptal ederse, it değişkeni boş olarak ayarlanır ve result cancel döner.

Örnek:
ask "Adın ne senin bakayım?"

Not: it değişkeni, get, read from file, convert, ask ve answer gibi belirli komutların sonucunu almak için kullanılan özel bir yerel değişkendir. it değişkeni diğer yerel değişkenler gibi kullanılabilir: içine bir değer koyabilir veya başka bir değişkene koyabilirsiniz.



Dosya Seçme Diyalogları


Dosya seçme diyalogları, sistem standart diyaloglarını görüntülemenizi sağlar. Bu diyaloglar kullanıcının bir dosya veya dosya kümesi seçmesine, bir klasör seçmesine veya bir dosyayı kaydetmek için bir ad ve konum belirlemesine olanak tanır.


Answer File Diyaloğu




answer file komutu, kullanıcının bir dosya seçmesi için standart bir dosya iletişim kutusu görüntüler. Bir bilgi istemi mesajı ve isteğe bağlı bir defaultPath belirtebilirsiniz.

Kullanıcı tarafından seçilen dosyanın dosya yolu, it değişkeninde döndürülür. Kullanıcı diyaloğu iptal ettiyse, it değişkeni boş olur ve result cancel döndürülür.

Örnek:
answer file "İşlem yapmak istediğiniz dosyayı seçiniz:"



Ask File Diyaloğu



ask file komutu, kullanıcının bir dosya adı girmesi ve bir konum belirlemesi için standart bir Kaydet diyaloğu görüntüler. Bir istek mesajı ve isteğe bağlı bir defaultPath belirtebilirsiniz.

Kullanıcı tarafından seçilen dosyanın dosya yolu, it değişkeninde döndürülür. Kullanıcı diyaloğu iptal ettiyse, it değişkeni boş olur ve result cancel döndürülür.

Örnek:
ask file "Kayıt dosya adını giriniz:"



Answer Folder Diyaloğu



answer folder komutu, kullanıcının bir klasör seçmesi için standart bir dosya diyaloğu görüntüler. Bir istek mesajı ve isteğe bağlı bir defaultPath belirtebilirsiniz.

Kullanıcı tarafından seçilen klasör dosya yolu, it değişkeninde döndürülür. Kullanıcı diyaloğu iptal ettiyse, it değişkeni boş olur ve result cancel döndürülür.

Örnek:
answer folder "Lütfen bir klasör seçin:"



Answer Color Diyaloğu



answer color komutu, işletim sisteminin standart renk seçimi diyaloğunu görüntüleyerek kullanıcının bir renk seçmesine olanak tanır. İsteğe bağlı bir beginColor belirtebilirsiniz. Bir beginColor belirtirseniz, diyalog varsayılan olarak bu rengi görüntüler.

Kullanıcının seçtiği renk, it değişkenine yerleştirilir. Kullanıcı diyaloğu iptal ederse, it değişkeni boş olarak ayarlanır ve result cancel döndürülür.

Renk, sıfır ile 255 arasında virgülle ayrılmış üç tam sayı biçiminde döndürülür ve kırmızı, yeşil ve mavinin her birinin düzeyini belirtir. Bu format, herhangi bir renk özelliğini ayarlamak için doğrudan kullanılabilir.

Örnek:
answer color with "blue"



Temel bilgiler bu kadar. Bu platform hoşuma gitti, burada daha ilerlerim gibi geliyor. Birlikte öğrenmeye devam etmek ümidiyle, kalın sağlıcakla..



















Hiç yorum yok:

Yorum Gönder