Tornado FX ile Gelişmiş Not Defteri Uygulaması (Kotlin)
Bu proje, Tornado FX kullanarak XML arayüzü ile gelişmiş bir not defteri uygulaması oluşturmayı hedefler. Uygulama temel CRUD (Create, Read, Update, Delete) işlemlerini destekleyecek ve ek özellikler içerecektir.
Temel Özellikler
Not oluşturma, görüntüleme, düzenleme ve silme
Notları kategorilere göre gruplama
Arama ve filtreleme özellikleri
Zengin metin düzenleme (bold, italic, renkler vb.)
Verilerin yerel depolamada saklanması
1. Proje Kurulumu
Öncelikle build.gradle.kts dosyasına gerekli bağımlılıkları ekleyelim:
plugins { kotlin("jvm") version "1.6.10" application } repositories { mavenCentral() } dependencies { implementation("no.tornado:tornadofx:1.7.20") implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.0") implementation("com.fasterxml.jackson.core:jackson-databind:2.13.1") implementation("org.slf4j:slf4j-simple:1.7.32") } application { mainClass.set("com.example.notdefteri.NotDefteriApp") }
2. Uygulama Sınıfı
package com.example.notdefteri import tornadofx.App import tornadofx.launch class NotDefteriApp : App(MainView::class, Styles::class) fun main(args: Array<String>) { launch<NotDefteriApp>(args) }
3. Ana Görünüm (MainView)
package com.example.notdefteri.views import tornadofx.* class MainView : View("Gelişmiş Not Defteri") { override val root = borderpane { top = menubar { menu("Dosya") { item("Yeni Not").action { /* Yeni not oluştur */ } item("Aç").action { /* Not aç */ } item("Kaydet").action { /* Kaydet */ } separator() item("Çıkış").action { close() } } menu("Düzenle") { item("Geri Al").action { /* Geri al */ } item("İleri Al").action { /* İleri al */ } } menu("Yardım") { item("Hakkında").action { /* Hakkında dialog */ } } } center = tabpane { tab("Tüm Notlar") { add(NotListesiView::class) } tab("Kategoriler") { add(KategoriView::class) } } } }
4. Not Modeli
package com.example.notdefteri.models import javafx.beans.property.SimpleObjectProperty import javafx.beans.property.SimpleStringProperty import tornadofx.* import java.time.LocalDateTime class Not( baslik: String = "", icerik: String = "", kategori: Kategori? = null ) { val baslikProperty = SimpleStringProperty(baslik) var baslik by baslikProperty val icerikProperty = SimpleStringProperty(icerik) var icerik by icerikProperty val kategoriProperty = SimpleObjectProperty(kategori) var kategori by kategoriProperty val olusturmaTarihiProperty = SimpleObjectProperty(LocalDateTime.now()) var olusturmaTarihi by olusturmaTarihiProperty val degistirmeTarihiProperty = SimpleObjectProperty(LocalDateTime.now()) var degistirmeTarihi by degistirmeTarihiProperty override fun toString() = baslik } class Kategori( ad: String = "", renk: String = "" ) { val adProperty = SimpleStringProperty(ad) var ad by adProperty val renkProperty = SimpleStringProperty(renk) var renk by renkProperty override fun toString() = ad }
5. Not Listesi Görünümü (XML)
notlistesi.fxml:
<?xml version="1.0" encoding="UTF-8"?> <?import javafx.scene.control.*?> <?import javafx.scene.layout.*?> <VBox xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1"> <HBox spacing="10" style="-fx-padding: 10;"> <TextField fx:id="aramaAlani" promptText="Notlarda ara..." HBox.hgrow="ALWAYS"/> <Button text="Ara" onAction="#notlariAra"/> <Button text="Yeni Not" onAction="#yeniNotEkle"/> </HBox> <SplitPane dividerPositions="0.3" VBox.vgrow="ALWAYS"> <items> <ListView fx:id="notListView"/> <VBox> <Label fx:id="notBaslikLabel" style="-fx-font-size: 16pt; -fx-font-weight: bold;"/> <Label fx:id="notTarihLabel" style="-fx-text-fill: gray;"/> <Label fx:id="notKategoriLabel"/> <Separator/> <TextArea fx:id="notIcerikArea" wrapText="true" VBox.vgrow="ALWAYS"/> <HBox spacing="10" style="-fx-padding: 10;" alignment="CENTER_RIGHT"> <Button text="Kaydet" onAction="#notuKaydet"/> <Button text="Sil" style="-fx-base: #ff4444;" onAction="#notuSil"/> </HBox> </VBox> </items> </SplitPane> </VBox>
NotListesiView.kt:
package com.example.notdefteri.views import tornadofx.* import javafx.scene.control.ListView import javafx.scene.control.TextArea import javafx.scene.control.TextField import com.example.notdefteri.models.Not import javafx.scene.control.Label class NotListesiView : View("Not Listesi") { private val notListView: ListView<Not> by fxid() private val notIcerikArea: TextArea by fxid() private val notBaslikLabel: Label by fxid() private val notTarihLabel: Label by fxid() private val notKategoriLabel: Label by fxid() private val aramaAlani: TextField by fxid() private val notlar = mutableListOf<Not>().observable() override val root = loadFXML("notlistesi.fxml") init { notListView.items = notlar notListView.selectionModel.selectedItemProperty().addListener { _, _, yeniNot -> yeniNot?.let { notuGoster(it) } } // Örnek veriler notlar.addAll( Not("Alışveriş Listesi", "Süt, yumurta, ekmek alınacak"), Not("Toplantı Notları", "Proje teslim tarihi: 15 Haziran"), Not("Kitap Önerileri", "1. Sapiens\n2. Homo Deus\n3. 21 Lessons for 21st Century") ) } private fun notuGoster(not: Not) { notBaslikLabel.text = not.baslik notTarihLabel.text = "Oluşturulma: ${not.olusturmaTarihi}" notKategoriLabel.text = "Kategori: ${not.kategori ?: "Kategorisiz"}" notIcerikArea.text = not.icerik } fun notlariAra() { val aramaMetni = aramaAlani.text.toLowerCase() if (aramaMetni.isBlank()) { notListView.items = notlar } else { notListView.items = notlar.filter { it.baslik.toLowerCase().contains(aramaMetni) || it.icerik.toLowerCase().contains(aramaMetni) }.observable() } } fun yeniNotEkle() { val yeniNot = Not("Yeni Not", "") notlar.add(yeniNot) notListView.selectionModel.select(yeniNot) notBaslikLabel.text = yeniNot.baslik notIcerikArea.text = yeniNot.icerik } fun notuKaydet() { val seciliNot = notListView.selectionModel.selectedItem ?: return seciliNot.apply { baslik = notBaslikLabel.text icerik = notIcerikArea.text degistirmeTarihi = LocalDateTime.now() } notListView.refresh() } fun notuSil() { val seciliNot = notListView.selectionModel.selectedItem ?: return notlar.remove(seciliNot) } }
6. Kategori Görünümü
kategoriview.fxml:
<?xml version="1.0" encoding="UTF-8"?> <?import javafx.scene.control.*?> <?import javafx.scene.layout.*?> <VBox xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1"> <HBox spacing="10" style="-fx-padding: 10;"> <TextField fx:id="kategoriAdiAlani" promptText="Kategori adı" HBox.hgrow="ALWAYS"/> <ColorPicker fx:id="kategoriRenkSecici"/> <Button text="Ekle" onAction="#kategoriEkle"/> </HBox> <ListView fx:id="kategoriListView" VBox.vgrow="ALWAYS"/> </VBox>
KategoriView.kt:
package com.example.notdefteri.views import tornadofx.* import javafx.scene.control.ListView import javafx.scene.control.TextField import javafx.scene.paint.Color import com.example.notdefteri.models.Kategori import javafx.scene.control.ColorPicker class KategoriView : View("Kategoriler") { private val kategoriListView: ListView<Kategori> by fxid() private val kategoriAdiAlani: TextField by fxid() private val kategoriRenkSecici: ColorPicker by fxid() private val kategoriler = mutableListOf<Kategori>().observable() override val root = loadFXML("kategoriview.fxml") init { kategoriListView.items = kategoriler // Örnek kategoriler kategoriler.addAll( Kategori("Kişisel", "#FF5733"), Kategori("İş", "#3385FF"), Kategori("Alışveriş", "#33FF57") ) } fun kategoriEkle() { val ad = kategoriAdiAlani.text if (ad.isNotBlank()) { val renk = String.format("#%02X%02X%02X", (kategoriRenkSecici.value.red * 255).toInt(), (kategoriRenkSecici.value.green * 255).toInt(), (kategoriRenkSecici.value.blue * 255).toInt()) val yeniKategori = Kategori(ad, renk) kategoriler.add(yeniKategori) kategoriAdiAlani.clear() } } }
7. Stiller (CSS)
Styles.kt:
package com.example.notdefteri import tornadofx.* class Styles : Stylesheet() { companion object { val heading by cssclass() } init { heading { padding = box(10.px) fontSize = 20.px fontWeight = FontWeight.BOLD } listView { cell { padding = box(5.px) } } } }
8. Veri Depolama (JSON)
Verileri JSON formatında kaydetmek ve yüklemek için:
package com.example.notdefteri.utils import com.fasterxml.jackson.module.kotlin.* import java.io.File object VeriDepolama { private val objectMapper = jacksonObjectMapper() fun notlariKaydet(notlar: List<Not>, dosyaYolu: String) { objectMapper.writeValue(File(dosyaYolu), notlar) } fun notlariYukle(dosyaYolu: String): List<Not> { return try { objectMapper.readValue<List<Not>>(File(dosyaYolu)) } catch (e: Exception) { emptyList() } } fun kategorileriKaydet(kategoriler: List<Kategori>, dosyaYolu: String) { objectMapper.writeValue(File(dosyaYolu), kategoriler) } fun kategorileriYukle(dosyaYolu: String): List<Kategori> { return try { objectMapper.readValue<List<Kategori>>(File(dosyaYolu)) } catch (e: Exception) { emptyList() } } }
9. Uygulamayı Geliştirmek İçin Ek Öneriler
Zengin Metin Düzenleme: HTML veya Markdown desteği ekleyin
Bulut Senkronizasyonu: Dropbox veya Google Drive entegrasyonu
Etiket Sistemi: Kategorilere ek olarak etiketler
Yedekleme ve Geri Yükleme: Otomatik yedekleme özelliği
Çoklu Dil Desteği: Uluslararasılaştırma (i18n)
Karanlık Mod: Kullanıcı arayüzü teması seçenekleri
Bu temel yapıyı kullanarak Tornado FX ve Kotlin ile gelişmiş bir not defteri uygulaması oluşturabilirsiniz. XML tabanlı arayüz sayesinde görsel düzenlemeler kolaylaşırken, Kotlin'in güçlü özellikleriyle arka plan işlemlerini verimli bir şekilde yönetebilirsiniz.
Hiç yorum yok:
Yorum Gönder