AMP Analytics -->

Teknoloji Rehberi

1 Mayıs 2025 Perşembe

Tornado FX ile Gelişmiş Not Defteri Uygulaması (Kotlin)

 

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:

kotlin
Copy
Download
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ı

kotlin
Copy
Download
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)

kotlin
Copy
Download
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

kotlin
Copy
Download
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
Copy
Download
Run
<?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:

kotlin
Copy
Download
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
Copy
Download
Run
<?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:

kotlin
Copy
Download
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:

kotlin
Copy
Download
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:

kotlin
Copy
Download
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

  1. Zengin Metin Düzenleme: HTML veya Markdown desteği ekleyin

  2. Bulut Senkronizasyonu: Dropbox veya Google Drive entegrasyonu

  3. Etiket Sistemi: Kategorilere ek olarak etiketler

  4. Yedekleme ve Geri Yükleme: Otomatik yedekleme özelliği

  5. Çoklu Dil Desteği: Uluslararasılaştırma (i18n)

  6. 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

Popular Posts