forked from pisilinux-rs/yali-rs
Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 22de7d5255 | |||
| 75e2b1bf5e | |||
| dcd3ec4efb | |||
| 7209d58ea0 | |||
| 0a02f576d4 | |||
| 5c148472c4 |
@@ -0,0 +1,73 @@
|
||||
# AGENTS.md — Yali-rs Geliştirme Notları
|
||||
|
||||
> Ajan oturumları arasında süreklilik için. Son güncelleme: parted MiB düzeltmesi (23 Ağu)
|
||||
|
||||
## Proje
|
||||
- PisiLinux kurulum aracı (eski Python Yali'nin Rust/egui yeniden yazımı).
|
||||
- Ana dallar: `main`. Push: `git push origin main`.
|
||||
- Referans kod: `/media/yedekleme/github/erkanisik/yali` (eski Python Yali —
|
||||
storage mimarisi: `yali/storage/{operations,partitioning,devicetree}.py`).
|
||||
|
||||
## Mimari Kararlar (2026-08 dönemi)
|
||||
|
||||
### Çoklu Disk Manuel Bölümleme
|
||||
- `CustomPartition` alanları (`src/installer.rs`):
|
||||
- `disk`: bölümün ait olduğu disk ("/dev/sda") — gruplama bununla.
|
||||
- `orig_device`: lsblk'tan gelen fiziksel cihaz adı; **boş = kullanıcı tanımı yeni bölüm**.
|
||||
Silme/işaret mantığı buna dayanır.
|
||||
- `delete: bool`: eski Yali `OperationDestroyDevice` karşılığı → kurulumda `parted rm`.
|
||||
- UI (`src/steps/partition.rs`):
|
||||
- Manuel moda girişte TÜM diskler otomatik taranıp listelenir (disk kartına tıklamak
|
||||
artık sadece "yeni bölüm hedef diski" seçer; tanımları sıfırlamaz — merge eder,
|
||||
`orig_device` eşleşmesinde kullanıcı tanımı kazanır).
|
||||
- Tablo `TableRow::Part | TableRow::FreeSpace` enum'u ile kurulur; HER disk grubu
|
||||
kendi sütun başlıklarını çizer (`draw_partition_table_header`).
|
||||
- Silinmek işaretli bölümler ayrı satır çizmez; İLK silinenin konumunda TEK
|
||||
"Boş Alan" satırında birleşir (GParted tarzı).
|
||||
`Boş Alan = disk − (korunan fiziksel + yeni tanımlar)`; 0 ise satır gizlenir.
|
||||
- "Kalan alan" SEMBOLİKTİR (size_mb=0): kaydetme anında sayıya çevrilmez!
|
||||
Tabloda/özetde kapasite olarak gösterilir; gerçek boyut kurulum anında boşluktan alınır.
|
||||
(Somutlaştırma taşmaya yol açtığı için kaldırıldı — swap sonradan büyütülünce kök patlıyordu.)
|
||||
- Slot ataması (`installer::assign_partition_numbers`): parted'ın gerçek davranışını
|
||||
simüle eder — fiziksel (silinmeyen) bölümler numarasını korur, silinenlerin slotu
|
||||
boşalır, yeni tanımlar en küçük boş slotu alır. UI ile job tutarlılığı buradan gelir.
|
||||
|
||||
### Granular Kurulum Job'ı (`jobs/partition.rs::CustomPartitionJob`)
|
||||
Eski wipe&rebuild (mktable + hepsini baştan yarat) KALDIRILDI. Yeni fazlar:
|
||||
1. FAZ A: `delete=true` olanlar `parted rm N` — N, `orig_device`'tan;
|
||||
**büyükten küçüğe** sil (kernel slot kayması).
|
||||
2. FAZ B: yeni tanımlar `disk_free_regions_mb()` ile sysfs'ten hesaplanan boş
|
||||
bölgelere first-fit yerleşir ("kalan" = en büyük bölge). Sabit boyutlular ÖNCE,
|
||||
sembolikler SONRA (`creates.sort_by_key(size==0)`) işlenir.
|
||||
3. FAZ C: yalnızca istenenler mkfs (LUKS akışı korundu).
|
||||
- İşlemi olmayan disklere ve `delete=true` satırlara mount/fstab/summary dokunmaz.
|
||||
|
||||
### Diğer Düzeltmeler
|
||||
- **parted birim tuzağı (KRİTİK):** parted `"MB"` sonekini **ondalık** (10^6 byte) okur,
|
||||
bizim hesaplar binary (2^20). `mkpart 3944MB` → sektör 7703125 (hizasız, boş alana
|
||||
uymaz) → "Becerebildiğimiz buna en yakın konum..." hatası. ÇÖZÜM: parted'a daima
|
||||
`"{}MiB"` sonekiyle geç (`jobs/partition.rs`, hem otomatik hem CustomPartitionJob).
|
||||
Tamsayı MiB = 2048 sektörün katı → hizalama hatası çıkmaz. UI'da kullanıcıya MB
|
||||
gösterilir ama parted argümanı asla "MB" olmasın.
|
||||
- MB dönüşümleri tek bölme: `x / 1_048_576` (çift `x/1024/1024` hassasiyet kaybı yapardı).
|
||||
- Disk seçiminde `size_gb * 1024` yerine `size_bytes / 1_048_576`.
|
||||
- Mountpoint değişince boyut alanı SADECE boş/"0"/"remaining" ise varsayılanlanır
|
||||
(kullanıcının elle girdiği değer ezilmez).
|
||||
- Simge fontu: `assets/remixicon.ttf` egui fallback fontu (`ui/theme.rs setup_fonts`).
|
||||
Kutucuk görürsen karakter fontta yok demektir — fontTools ile cmap kontrol et:
|
||||
kullanılanlar: EB7B check, EA6B arrow, EC28 trash, EA13 add, EFE0 pencil, EECD lock,
|
||||
ECA1 error, EDFB hard-drive-line, ECB5/B7 eye, F4C6 close.
|
||||
- Alt bar: "Mevcut Durum / Kurulum Sonrası" çift şerit (`manual_dual_bars`,
|
||||
gri segment = boş) — SADECE seçili disk için gösterilir (`state.selected_disk`).
|
||||
|
||||
## Bilinen Açıklar / Yapılacaklar
|
||||
- [ ] VM testinde rapor edilen hatalar düzeltilecek (kullanıcı listeleyecek)
|
||||
- [ ] `manual_capacity_error` yalnızca sabit boyutları kontrol eder; LVM PV/LV toplamları ayrıca doğrulanmalı
|
||||
- [ ] Silinmek işaretli bölüm bir VG'nin PV'siyse SetupLvmJob tarafında kontrol yok
|
||||
- [ ] Otomatik mod (erase disk) hâlâ tek disk; çoklu otomatik plan yok
|
||||
- [ ] Debug amaçlı kalan `eprintln!` satırları temizlenebilir
|
||||
|
||||
## Test / Derleme
|
||||
- `cargo check` + `cargo test` (22 birim testi; partition modülü ağırlıklı).
|
||||
- VM'de denemek: ISO'yu boot edip manuel bölümlemede sdb üzerindeki senaryolar:
|
||||
sil→boş-alan birleşimi, geri al, kalan alan, farklı diske bölüm ekleme.
|
||||
@@ -141,6 +141,13 @@ display_manager_autologin_hint = "(first user)"
|
||||
display_manager_de_not_found = "(not detected)"
|
||||
display_manager_note = "Note: The display manager will be enabled by default on the installed system."
|
||||
mp_size_remaining = "Remaining Space"
|
||||
mp_size_available = "Available"
|
||||
mp_create_here = "Create Here"
|
||||
mp_will_delete = "will be deleted"
|
||||
mp_restore = "Restore"
|
||||
mp_free_space = "Free Space"
|
||||
mp_bar_now = "Current Layout"
|
||||
mp_bar_after = "After Installation"
|
||||
|
||||
# ── UI / General ──────────────────────────────────────────
|
||||
installer = "YALI Installation Tool"
|
||||
@@ -235,6 +242,14 @@ mp_skip_format = "Skip Formatting (preserve existing data)"
|
||||
mp_size_gb = "%{n} GB"
|
||||
mp_size_mb = "%{n} MB"
|
||||
|
||||
# ── Table Column Headers ────────────────────────────────────
|
||||
column_device = "Device"
|
||||
column_mount_point = "Mount Point"
|
||||
column_label = "Label"
|
||||
column_type = "Type"
|
||||
column_format = "Format"
|
||||
column_size = "Size"
|
||||
|
||||
|
||||
check_ram = "RAM"
|
||||
check_ram_fail = "%{mb} MB RAM available. Minimum 1 GB required."
|
||||
|
||||
@@ -117,6 +117,14 @@ disk_too_small = "Disk çok küçük"
|
||||
summary_partition_plan = "Bölümleme"
|
||||
summary_custom_partitions = "Manuel Bölümler"
|
||||
mp_size_remaining = "Kalan Alan"
|
||||
mp_size_available = "Kullanılabilir"
|
||||
mp_create_here = "Buraya Oluştur"
|
||||
mp_will_delete = "silinecek"
|
||||
mp_restore = "Geri Al"
|
||||
mp_free_space = "Boş Alan"
|
||||
mp_bar_now = "Mevcut Durum"
|
||||
mp_bar_after = "Kurulum Sonrası"
|
||||
|
||||
|
||||
# ── UI / Genel ────────────────────────────────────────────
|
||||
installer = "PisiLinux Kurulum Aracı"
|
||||
@@ -206,6 +214,14 @@ mp_skip_format = "Biçimlendirmeyi Atla (mevcut veri korunur)"
|
||||
mp_size_gb = "%{n} GB"
|
||||
mp_size_mb = "%{n} MB"
|
||||
|
||||
# ── Tablo Sütun Başlıkları ────────────────────────────────────
|
||||
column_device = "Aygıt"
|
||||
column_mount_point = "Bağlama Noktası"
|
||||
column_label = "Etiket"
|
||||
column_type = "Tür"
|
||||
column_format = "Biçim"
|
||||
column_size = "Boyut"
|
||||
|
||||
|
||||
check_ram = "RAM"
|
||||
check_ram_fail = "%{mb} MB RAM mevcut. Kurulum için minimum 1 GB gerekli."
|
||||
|
||||
+81
-1
@@ -113,6 +113,7 @@ impl std::fmt::Display for PartitionTableType {
|
||||
|
||||
/// Manuel bölümleme için desteklenen dosya sistemleri.
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
#[allow(dead_code)]
|
||||
pub enum FsType {
|
||||
Ext4,
|
||||
Ext3,
|
||||
@@ -122,7 +123,6 @@ pub enum FsType {
|
||||
Ntfs,
|
||||
Swap,
|
||||
Lvm, // LVM fiziksel bölüm (PV)
|
||||
#[allow(dead_code)]
|
||||
LvmLv, // LVM mantıksal bölüm (LV)
|
||||
}
|
||||
|
||||
@@ -150,6 +150,7 @@ impl std::fmt::Display for FsType {
|
||||
|
||||
/// LVM Volume Group yapılandırması.
|
||||
#[derive(Clone, Debug, Default)]
|
||||
#[allow(dead_code)]
|
||||
pub struct VolumeGroup {
|
||||
pub name: String,
|
||||
pub pv_devices: Vec<String>, // PV olarak kullanılacak bölüm aygıtları
|
||||
@@ -167,11 +168,86 @@ pub struct LogicalVolume {
|
||||
pub mountpoint: String,
|
||||
}
|
||||
|
||||
/// Cihaz yolundan ait olduğu diskin yolunu çıkarır.
|
||||
///
|
||||
/// - `/dev/sda1` → `/dev/sda`
|
||||
/// - `/dev/nvme0n1p2` → `/dev/nvme0n1`
|
||||
/// - `/dev/mmcblk0p1` → `/dev/mmcblk0`
|
||||
#[allow(dead_code)] // genel yardımcı; testlerde ve gelecekteki işlerde kullanılır
|
||||
pub fn device_to_disk(device: &str) -> String {
|
||||
let base = device.trim_start_matches("/dev/");
|
||||
// NVMe/MMC/loop aygıtlarında bölüm ayracı "p" + rakamdır
|
||||
let needs_p = base.contains("nvme") || base.contains("mmcblk") || base.contains("loop");
|
||||
if let Some(pos) = base.rfind('p') {
|
||||
if needs_p && base[pos + 1..].chars().all(|c| c.is_ascii_digit()) && !base[pos + 1..].is_empty() {
|
||||
return format!("/dev/{}", &base[..pos]);
|
||||
}
|
||||
}
|
||||
// SATA/virtio: sondaki tüm rakamları at
|
||||
let trimmed = base.trim_end_matches(|c: char| c.is_ascii_digit());
|
||||
format!("/dev/{}", trimmed)
|
||||
}
|
||||
|
||||
/// Cihaz adından bölüm numarasını çıkarır ("/dev/sda3" → 3,
|
||||
/// "/dev/nvme0n1p2" → 2). Sonda rakam yoksa None döner.
|
||||
pub fn part_number(device: &str) -> Option<u32> {
|
||||
let base = device.trim_start_matches("/dev/");
|
||||
let digits: String = base.chars().rev().take_while(|c| c.is_ascii_digit()).collect();
|
||||
digits.chars().rev().collect::<String>().parse().ok()
|
||||
}
|
||||
|
||||
/// Bölüm tanımlarına parted'ın gerçek slot davranışını izleyerek
|
||||
/// aygıt numarası atar.
|
||||
///
|
||||
/// Eski Yali modeliyle aynı mantık: fiziksel (silinmemiş) bölümler
|
||||
/// kendi numaralarını korur; silinmek işaretlenenlerin slotları boşalır
|
||||
/// ve yeni tanımlar en küçük boş slotu sırayla doldurur. Örn. sda2
|
||||
/// silinip yeni bölüm eklenirse yeni bölüm sda2 olur; sda3 yerinde kalır.
|
||||
pub fn assign_partition_numbers(parts: &mut [CustomPartition]) {
|
||||
use std::collections::BTreeSet;
|
||||
// Disk bazlı kullanılan slot kümeleri
|
||||
let mut used: std::collections::HashMap<String, BTreeSet<u32>> = std::collections::HashMap::new();
|
||||
for p in parts.iter() {
|
||||
if p.delete { continue; } // silinecekler slot açar
|
||||
if !p.orig_device.is_empty() { // fiziksel: kendi numarası
|
||||
if let Some(n) = part_number(&p.orig_device) {
|
||||
used.entry(p.disk.clone()).or_default().insert(n);
|
||||
}
|
||||
}
|
||||
}
|
||||
for p in parts.iter_mut() {
|
||||
if p.delete {
|
||||
// Silinecek bölüm: kurulumda yok olacak; adı olduğu gibi kalır,
|
||||
// yeni slot tüketmez.
|
||||
continue;
|
||||
}
|
||||
match part_number(&p.orig_device) {
|
||||
Some(n) => { p.device = part_path(&p.disk, n); }
|
||||
None => {
|
||||
let set = used.entry(p.disk.clone()).or_default();
|
||||
// en küçük boş slot
|
||||
let mut n = 1u32;
|
||||
while set.contains(&n) { n += 1; }
|
||||
set.insert(n);
|
||||
p.device = part_path(&p.disk, n);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Kullanıcının manuel olarak tanımladığı tek bir bölüm.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct CustomPartition {
|
||||
/// Bölümün ait olduğu disk (ör. "/dev/sda").
|
||||
/// Çoklu disk kurulumunda bölümler diske göre gruplanır.
|
||||
pub disk: String,
|
||||
/// Bölüm aygit adı; oluşturulacak (ör. "/dev/sda1")
|
||||
pub device: String,
|
||||
/// Taramada bulunan fiziksel cihaz adı (örn. "/dev/sda3").
|
||||
/// Kullanıcı tarafından yeni eklenen tanımlarda boştur.
|
||||
/// Silme işleminde satırın listede kalıp yeniden
|
||||
/// tanımlanabilmesini sağlar.
|
||||
pub orig_device: String,
|
||||
/// Boyut MB cinsinden; 0 = "kalan alan"
|
||||
pub size_mb: u64,
|
||||
/// Dosya sistemi
|
||||
@@ -186,6 +262,10 @@ pub struct CustomPartition {
|
||||
pub luks_name: String,
|
||||
/// false = biçimlendir, true = olduğu gibi bırak (mevcut bölümleri koru)
|
||||
pub skip_format: bool,
|
||||
/// true = kurulum sırasında bu bölüm diskten SİLİNECEK
|
||||
/// (eski Yali'deki OperationDestroyDevice karşılığı).
|
||||
/// Satır listede görünür kalır; istenirse işaret geri alınır.
|
||||
pub delete: bool,
|
||||
}
|
||||
|
||||
/// Kurulum boyunca toplanan tüm veriler.
|
||||
|
||||
+1
-1
@@ -633,7 +633,7 @@ pub fn build_custom_job_queue(
|
||||
let src = if cfg.source.is_empty() { detect_source_dir() } else { cfg.source };
|
||||
|
||||
let mut jobs: Vec<Box<dyn Job>> = vec![
|
||||
Box::new(CustomPartitionJob { disk: disk.clone(), custom_partitions: custom_partitions.clone() }),
|
||||
Box::new(CustomPartitionJob { custom_partitions: custom_partitions.clone() }),
|
||||
Box::new(SetupLvmJob { volume_groups: volume_groups.clone(), logical_volumes: logical_volumes.clone() }),
|
||||
Box::new(MountCustomPartitionsJob { custom_partitions: custom_partitions.clone(), logical_volumes: logical_volumes.clone(), mount: mt.clone() }),
|
||||
Box::new(MountBindJob { mount: mt.clone() }),
|
||||
|
||||
+210
-66
@@ -204,15 +204,15 @@ impl Job for PartitionJob {
|
||||
ui.log(format!("parted {} mktable {}", disk, table_label));
|
||||
run_parted(&["-s", disk, "mktable", table_label]).await?;
|
||||
|
||||
let mut next_mb: u64 = 1; // İlk MB'ı boot kayıtları için bırak
|
||||
let mut next_mb: u64 = 1; // İlk MiB'ı boot kayıtları için bırak
|
||||
|
||||
// 2. EFI bölümü (yalnızca GPT/UEFI)
|
||||
if self.plan.efi_mb > 0 {
|
||||
let end_mb = next_mb + self.plan.efi_mb;
|
||||
ui.log(format!("EFI bölümü oluşturuluyor: {}–{} MB", next_mb, end_mb));
|
||||
ui.log(format!("EFI bölümü oluşturuluyor: {}–{} MiB", next_mb, end_mb));
|
||||
run_parted(&[
|
||||
"-s", disk, "mkpart", "fat32",
|
||||
&format!("{}MB", next_mb), &format!("{}MB", end_mb),
|
||||
&format!("{}MiB", next_mb), &format!("{}MiB", end_mb),
|
||||
]).await?;
|
||||
run_parted(&["-s", disk, "set", "1", "esp", "on"]).await?;
|
||||
next_mb = end_mb;
|
||||
@@ -221,16 +221,16 @@ impl Job for PartitionJob {
|
||||
// 3. Swap bölümü
|
||||
let swap_end = next_mb + self.plan.swap_mb;
|
||||
let swap_num: u32 = if self.plan.efi_mb > 0 { 2 } else { 1 };
|
||||
ui.log(format!("Swap bölümü oluşturuluyor: {}–{} MB", next_mb, swap_end));
|
||||
ui.log(format!("Swap bölümü oluşturuluyor: {}–{} MiB", next_mb, swap_end));
|
||||
run_parted(&[
|
||||
"-s", disk, "mkpart", "linux-swap",
|
||||
&format!("{}MB", next_mb), &format!("{}MB", swap_end),
|
||||
&format!("{}MiB", next_mb), &format!("{}MiB", swap_end),
|
||||
]).await?;
|
||||
next_mb = swap_end;
|
||||
|
||||
// 4. Kök bölümü (kalan alan)
|
||||
let root_num: u32 = if self.plan.efi_mb > 0 { 3 } else { 2 };
|
||||
ui.log(format!("Kök bölümü oluşturuluyor: {}MB – disk sonu", next_mb));
|
||||
ui.log(format!("Kök bölümü oluşturuluyor: {}MiB – disk sonu", next_mb));
|
||||
run_parted(&[
|
||||
"-s", disk, "mkpart", "ext4",
|
||||
&format!("{}MB", next_mb), "100%",
|
||||
@@ -448,9 +448,75 @@ async fn read_partition_uuids(
|
||||
// MANUEL BÖLÜMLEME İÇİN DESTEKLEYİCİ JOB'LAR
|
||||
// ─────────────────────────────────────────────
|
||||
|
||||
/// Manuel bölümleme ayarlarını disk üzerinde uygulayan ve biçimlendiren Job.
|
||||
/// Diskteki boş bölgeleri MB cinsinden hesaplar (sysfs tabanlı).
|
||||
///
|
||||
/// `exclude_nums`: silinecek işaretli bölümler — boşluk olarak
|
||||
/// davranılır (kurulum sırasında önce silindikleri için).
|
||||
/// Dönen değer: (başlangıç_mb, bitiş_mb) aralıkları; disk sonunda
|
||||
/// GPT yedek başlığı için 1MB pay bırakılır.
|
||||
fn disk_free_regions_mb(disk: &str, exclude_nums: &[u32]) -> Result<Vec<(u64, u64)>, String> {
|
||||
let base = disk.trim_start_matches("/dev/");
|
||||
const SECTOR: u64 = 512;
|
||||
const MB: u64 = 1024 * 1024;
|
||||
|
||||
let disk_sectors: u64 = std::fs::read_to_string(format!("/sys/block/{}/size", base))
|
||||
.map_err(|e| format!("{} boyutu okunamadı: {}", disk, e))?
|
||||
.trim().parse().unwrap_or(0);
|
||||
if disk_sectors == 0 {
|
||||
return Err(format!("{} disk boyutu okunamadı", disk));
|
||||
}
|
||||
let total_mb = disk_sectors * SECTOR / MB;
|
||||
let usable_total = total_mb.saturating_sub(1); // disk sonu güvenlik payı
|
||||
|
||||
// Dolu aralıklar
|
||||
let mut occupied: Vec<(u64, u64)> = Vec::new();
|
||||
if let Ok(entries) = std::fs::read_dir(format!("/sys/block/{}", base)) {
|
||||
for entry in entries.flatten() {
|
||||
let name = entry.file_name().to_string_lossy().to_string();
|
||||
if !name.starts_with(base) || name == base { continue; }
|
||||
let num = match crate::installer::part_number(&format!("/dev/{}", name)) {
|
||||
Some(n) => n,
|
||||
None => continue,
|
||||
};
|
||||
if exclude_nums.contains(&num) { continue; }
|
||||
let start_sectors: u64 = std::fs::read_to_string(entry.path().join("start"))
|
||||
.ok().and_then(|s| s.trim().parse().ok()).unwrap_or(0);
|
||||
let len_sectors: u64 = std::fs::read_to_string(entry.path().join("size"))
|
||||
.ok().and_then(|s| s.trim().parse().ok()).unwrap_or(0);
|
||||
if len_sectors == 0 { continue; }
|
||||
let start_mb = (start_sectors * SECTOR + MB - 1) / MB;
|
||||
let end_mb = ((start_sectors + len_sectors) * SECTOR) / MB;
|
||||
if end_mb > start_mb {
|
||||
occupied.push((start_mb, end_mb.min(usable_total)));
|
||||
}
|
||||
}
|
||||
}
|
||||
occupied.sort();
|
||||
|
||||
// Boşlukları çıkar (ilk 1MB boot alanı hariç)
|
||||
const HEADROOM_MB: u64 = 1;
|
||||
let mut regions: Vec<(u64, u64)> = Vec::new();
|
||||
let mut cursor = HEADROOM_MB.min(usable_total);
|
||||
for (s, e) in &occupied {
|
||||
if *s > cursor { regions.push((cursor, *s)); }
|
||||
cursor = cursor.max(*e);
|
||||
}
|
||||
if cursor < usable_total { regions.push((cursor, usable_total)); }
|
||||
regions.retain(|(s, e)| e > s);
|
||||
Ok(regions)
|
||||
}
|
||||
|
||||
/// Manuel bölümleme ayarlarını granular (eski Yali Operation modeli)
|
||||
/// yaklaşımla uygulayan Job.
|
||||
///
|
||||
/// Eski Yali'nin DeviceTree/Operation mimarisiyle aynı felsefe:
|
||||
/// - Dokunulmayan disklere ve bölümlere HİÇ dokunulmaz (veriler korunur)
|
||||
/// - Silinmek işaretli bölümler `parted rm` ile kaldırılır
|
||||
/// (OperationDestroyDevice karşılığı)
|
||||
/// - Yeni tanımlar diskin BOŞ alanlarına yerleştirilir; tüm disk
|
||||
/// sıfırlanmaz (OperationCreateDevice karşılığı)
|
||||
/// - skip_format=false olanlar biçimlendirilir (OperationCreateFormat)
|
||||
pub struct CustomPartitionJob {
|
||||
pub disk: String,
|
||||
pub custom_partitions: Vec<CustomPartition>,
|
||||
}
|
||||
|
||||
@@ -460,68 +526,145 @@ impl Job for CustomPartitionJob {
|
||||
|
||||
async fn run(&self, ui: &UiSender) -> Result<(), String> {
|
||||
let is_uefi = std::path::Path::new("/sys/firmware/efi").exists();
|
||||
|
||||
// 0. Diski hazırla: bağlamaları ayır, kernel tablosunu temizle
|
||||
prepare_disk(&self.disk, ui).await;
|
||||
|
||||
let is_gpt = is_uefi; // Genellikle UEFI ise GPT kullanılır
|
||||
// MBR'de "primary" zorunludur, GPT'de ise bu alan isim (label) alanıdır.
|
||||
let part_type_or_name = if is_gpt { "" } else { "primary" };
|
||||
let part_type_or_name = if is_uefi { "" } else { "primary" };
|
||||
|
||||
// 1. Bölüm tablosu oluştur
|
||||
let table_label = if is_uefi { "gpt" } else { "msdos" };
|
||||
ui.log(format!("parted {} mktable {}", self.disk, table_label));
|
||||
run_parted(&["-s", &self.disk, "mktable", table_label]).await?;
|
||||
|
||||
let mut next_mb: u64 = 1; // İlk MB boot sektörü
|
||||
|
||||
// 2. Bölümleri parted ile oluştur
|
||||
for (i, part) in self.custom_partitions.iter().enumerate() {
|
||||
let part_num = (i + 1) as u32;
|
||||
let end_str = if part.size_mb == 0 {
|
||||
"100%".to_string()
|
||||
} else {
|
||||
format!("{}MB", next_mb + part.size_mb)
|
||||
};
|
||||
let start_str = format!("{}MB", next_mb);
|
||||
|
||||
let fs_label = match part.fstype {
|
||||
FsType::Fat32 => "fat32",
|
||||
FsType::Swap => "linux-swap",
|
||||
FsType::Ext4 => "ext4",
|
||||
FsType::Ext3 => "ext3",
|
||||
FsType::Btrfs => "btrfs",
|
||||
FsType::Xfs => "xfs",
|
||||
FsType::Ntfs => "ntfs",
|
||||
// LVM PV için parted "ext4" ile işaretle, pvcreate sonra
|
||||
FsType::Lvm => "ext4",
|
||||
// LVM LV doğrudan parted ile oluşturulmaz
|
||||
FsType::LvmLv => continue,
|
||||
};
|
||||
|
||||
ui.log(format!(
|
||||
"Bölüm {} oluşturuluyor: {} - {} ({})",
|
||||
part_num, start_str, end_str, fs_label
|
||||
));
|
||||
run_parted(&[
|
||||
"-s", &self.disk, "mkpart", part_type_or_name, fs_label,
|
||||
&start_str, &end_str
|
||||
]).await?;
|
||||
|
||||
if part.mountpoint == "/boot/efi" || part.fstype == FsType::Fat32 {
|
||||
run_parted(&["-s", &self.disk, "set", &part_num.to_string(), "esp", "on"]).await?;
|
||||
}
|
||||
|
||||
if part.size_mb > 0 {
|
||||
next_mb += part.size_mb;
|
||||
// 0. İşlem yapılan diskleri tanım sırasına göre topla
|
||||
let mut disks: Vec<String> = Vec::new();
|
||||
for part in &self.custom_partitions {
|
||||
if !disks.contains(&part.disk) {
|
||||
disks.push(part.disk.clone());
|
||||
}
|
||||
}
|
||||
|
||||
// 2.5. Tüm mkpart bitti; kernel'e bildir ve aygıt düğümlerini bekle
|
||||
sync_partition_table(&self.disk).await;
|
||||
for disk in &disks {
|
||||
let parts: Vec<&CustomPartition> = self.custom_partitions.iter()
|
||||
.filter(|p| p.disk == *disk).collect();
|
||||
|
||||
// 3. Biçimlendir (LUKS ile şifreleme desteklenir)
|
||||
// Bu disk için planlanan işlemler
|
||||
let destroys: Vec<u32> = parts.iter()
|
||||
.filter(|p| p.delete && !p.orig_device.is_empty())
|
||||
.filter_map(|p| crate::installer::part_number(&p.orig_device))
|
||||
.collect();
|
||||
let mut creates: Vec<&CustomPartition> = parts.iter()
|
||||
.filter(|p| !p.delete && p.orig_device.is_empty() && p.fstype != FsType::LvmLv)
|
||||
.map(|p| *p)
|
||||
.collect();
|
||||
// Sabit boyutlular ÖNCE yerleşir; "kalan alan" (size 0)
|
||||
// tanımları EN SON, geriye kalan boşluğa yerleştirilir.
|
||||
// (Stable sort → aynı grupta tanım sırası korunur.)
|
||||
creates.sort_by_key(|p| p.size_mb == 0);
|
||||
let has_format = parts.iter().any(|p| !p.delete && !p.skip_format);
|
||||
|
||||
if destroys.is_empty() && creates.is_empty() && !has_format {
|
||||
ui.log(format!("⏭️ {}: işlem yok, korunuyor", disk));
|
||||
continue; // eski Yali gibi dokunulmayan diske girilmez
|
||||
}
|
||||
|
||||
ui.log(format!("── Disk işleniyor: {} ──", disk));
|
||||
// Bağlamaları ayır, swap kapat
|
||||
prepare_disk(disk, ui).await;
|
||||
|
||||
// ── FAZ A: SİLMEK İŞARETLİ BÖLÜMLER (büyükten küçüğe numara) ──
|
||||
let mut del_sorted = destroys.clone();
|
||||
del_sorted.sort_unstable_by(|a, b| b.cmp(a));
|
||||
for n in &del_sorted {
|
||||
ui.log(format!("parted {} rm {} (bölüm siliniyor)", disk, n));
|
||||
run_parted(&["-s", disk, "rm", &n.to_string()]).await?;
|
||||
}
|
||||
if !del_sorted.is_empty() {
|
||||
sync_partition_table(disk).await;
|
||||
}
|
||||
|
||||
// ── FAZ B: YENİ BÖLÜMLER (boş alana yerleştir) ──────────────
|
||||
if !creates.is_empty() {
|
||||
let mut regions = disk_free_regions_mb(disk, &destroys)?;
|
||||
for part in &creates {
|
||||
let fs_label = match part.fstype {
|
||||
FsType::Fat32 => "fat32",
|
||||
FsType::Swap => "linux-swap",
|
||||
FsType::Ext4 => "ext4",
|
||||
FsType::Ext3 => "ext3",
|
||||
FsType::Btrfs => "btrfs",
|
||||
FsType::Xfs => "xfs",
|
||||
FsType::Ntfs => "ntfs",
|
||||
// LVM PV için parted "ext4" ile işaretle, pvcreate sonra
|
||||
FsType::Lvm => "ext4",
|
||||
FsType::LvmLv => continue,
|
||||
};
|
||||
|
||||
// Yerleşim: boyut belirtilmişse sığan İLK boş bölge,
|
||||
// "kalan alan" ise en büyük bölgenin tamamı.
|
||||
let need = part.size_mb;
|
||||
let placement = if need == 0 {
|
||||
let idx = regions.iter().enumerate()
|
||||
.max_by_key(|(_, (s, e))| e.saturating_sub(*s))
|
||||
.map(|(i, _)| i);
|
||||
match idx {
|
||||
Some(i) => {
|
||||
let r = regions.remove(i);
|
||||
Some(r)
|
||||
}
|
||||
None => None,
|
||||
}
|
||||
} else {
|
||||
let mut found = None;
|
||||
for (i, (s, e)) in regions.iter().enumerate() {
|
||||
if e.saturating_sub(*s) >= need {
|
||||
found = Some((i, (*s, s + need)));
|
||||
break;
|
||||
}
|
||||
}
|
||||
match found {
|
||||
Some((i, seg)) => {
|
||||
let (_, e) = regions[i];
|
||||
let start = seg.0;
|
||||
let new_start = start + need;
|
||||
if e > new_start {
|
||||
regions[i] = (new_start, e);
|
||||
} else {
|
||||
regions.remove(i);
|
||||
}
|
||||
Some(seg)
|
||||
}
|
||||
None => None,
|
||||
}
|
||||
};
|
||||
|
||||
let (start_mb, end_mb) = placement.ok_or_else(|| format!(
|
||||
"{} üzerinde {} MB için yeterli boş alan yok", disk, need))?;
|
||||
|
||||
// DİKKAT: parted "MB" sonekini onalık (10^6) okur;
|
||||
// hesaplarımız binary (2^20) olduğu için "MiB" zorunlu.
|
||||
// Tamsayı MiB = 2048 sektörün katı → parted hizalama
|
||||
// hatası vermez.
|
||||
let start_str = format!("{}MiB", start_mb);
|
||||
let end_str = format!("{}MiB", end_mb);
|
||||
let slot = crate::installer::part_number(&part.device).unwrap_or(0);
|
||||
|
||||
ui.log(format!(
|
||||
"{} üzerinde yeni bölüm oluşturuluyor: {} - {} ({})",
|
||||
disk, start_str, end_str, fs_label
|
||||
));
|
||||
run_parted(&[
|
||||
"-s", disk, "mkpart", part_type_or_name, fs_label,
|
||||
&start_str, &end_str
|
||||
]).await?;
|
||||
|
||||
if part.mountpoint == "/boot/efi" || part.fstype == FsType::Fat32 {
|
||||
run_parted(&["-s", disk, "set", &slot.to_string(), "esp", "on"]).await?;
|
||||
}
|
||||
}
|
||||
sync_partition_table(disk).await;
|
||||
}
|
||||
|
||||
// ── FAZ C: BİÇİMLENDİRME aşağıdaki ortak döngüde ────────────
|
||||
}
|
||||
|
||||
// Biçimlendir (LUKS ile şifreleme desteklenir) — tüm diskler.
|
||||
// Silinmek işaretli bölümlere hiç dokunulmaz.
|
||||
for part in &self.custom_partitions {
|
||||
if part.delete { continue; }
|
||||
let path = &part.device;
|
||||
|
||||
// LUKS şifreleme (kök veya isteğe bağlı bölümler)
|
||||
@@ -667,7 +810,8 @@ impl Job for MountCustomPartitionsJob {
|
||||
|
||||
async fn run(&self, ui: &UiSender) -> Result<(), String> {
|
||||
// 1. Kök (/) bölümünü (fiziksel partition veya LVM LV) bul ve bağla
|
||||
let root_device = if let Some(root_part) = self.custom_partitions.iter().find(|p| p.mountpoint == "/") {
|
||||
let root_device = if let Some(root_part) = self.custom_partitions.iter()
|
||||
.find(|p| p.mountpoint == "/" && !p.delete) {
|
||||
self.device_path(root_part)
|
||||
} else if let Some(root_lv) = self.logical_volumes.iter().find(|lv| lv.mountpoint == "/") {
|
||||
format!("/dev/{}/{}", root_lv.vg_name, root_lv.name)
|
||||
@@ -687,7 +831,7 @@ impl Job for MountCustomPartitionsJob {
|
||||
let mut mount_items = Vec::new();
|
||||
|
||||
for part in &self.custom_partitions {
|
||||
if part.mountpoint != "/" && part.fstype != FsType::Swap && !part.mountpoint.is_empty() {
|
||||
if part.mountpoint != "/" && part.fstype != FsType::Swap && !part.mountpoint.is_empty() && !part.delete {
|
||||
mount_items.push(MountItem {
|
||||
device: self.device_path(part),
|
||||
mountpoint: part.mountpoint.clone(),
|
||||
@@ -747,7 +891,7 @@ impl Job for FstabFromCustomPartitionsJob {
|
||||
);
|
||||
|
||||
for part in &self.custom_partitions {
|
||||
if part.mountpoint.is_empty() { continue; }
|
||||
if part.mountpoint.is_empty() || part.delete { continue; }
|
||||
|
||||
let is_encrypted = part.encrypt && !part.luks_password.is_empty();
|
||||
|
||||
|
||||
+235
-1274
File diff suppressed because it is too large
Load Diff
+55
-15
@@ -43,15 +43,25 @@ impl InstallerStep for SummaryStep {
|
||||
};
|
||||
summary_row(ui, t!("summary_keyboard"), &kb);
|
||||
|
||||
let disk_str = match &state.selected_disk {
|
||||
Some(d) => {
|
||||
if state.erase_disk {
|
||||
format!("{} \u{EA6B} {}", d, t!("summary_erase_mode"))
|
||||
} else {
|
||||
format!("{} \u{EA6B} {}", d, t!("summary_manual_mode"))
|
||||
let disk_str = if state.erase_disk {
|
||||
match &state.selected_disk {
|
||||
Some(d) => format!("{} \u{EA6B} {}", d, t!("summary_erase_mode")),
|
||||
None => t!("summary_not_selected").to_string(),
|
||||
}
|
||||
} else if !state.custom_partitions.is_empty() {
|
||||
// Manuel modda tanım yapılan TÜM diskleri listele
|
||||
let mut disks: Vec<&str> = Vec::new();
|
||||
for part in &state.custom_partitions {
|
||||
if !disks.contains(&part.disk.as_str()) {
|
||||
disks.push(&part.disk);
|
||||
}
|
||||
}
|
||||
None => t!("summary_not_selected").to_string(),
|
||||
format!("{} \u{EA6B} {}", disks.join(", "), t!("summary_manual_mode"))
|
||||
} else {
|
||||
match &state.selected_disk {
|
||||
Some(d) => format!("{} \u{EA6B} {}", d, t!("summary_manual_mode")),
|
||||
None => t!("summary_not_selected").to_string(),
|
||||
}
|
||||
};
|
||||
summary_row(ui, t!("summary_disk"), &disk_str);
|
||||
|
||||
@@ -73,15 +83,45 @@ impl InstallerStep for SummaryStep {
|
||||
}
|
||||
} else if !state.custom_partitions.is_empty() {
|
||||
let mut plan_lines = Vec::new();
|
||||
// Bölümleri ait oldukları diske göre gruplu listele
|
||||
let mut seen_disks: Vec<&str> = Vec::new();
|
||||
for part in &state.custom_partitions {
|
||||
let size_str = if part.size_mb == 0 {
|
||||
t!("mp_size_remaining").to_string()
|
||||
} else if part.size_mb >= 1024 {
|
||||
format!("{:.1} GB", part.size_mb as f64 / 1024.0)
|
||||
} else {
|
||||
format!("{} MB", part.size_mb)
|
||||
};
|
||||
plan_lines.push(format!("{} \u{EA6B} {} ({}, {})", part.device, part.mountpoint, part.fstype, size_str));
|
||||
if !seen_disks.contains(&part.disk.as_str()) {
|
||||
seen_disks.push(&part.disk);
|
||||
}
|
||||
}
|
||||
for d in seen_disks {
|
||||
plan_lines.push(format!("\u{EDFB} {}", d));
|
||||
for part in state.custom_partitions.iter().filter(|p| p.disk == d) {
|
||||
let size_str = if part.size_mb == 0 {
|
||||
// Sembolik boyut: kalan kapasiteyi göster
|
||||
let others: u64 = state.custom_partitions.iter()
|
||||
.filter(|q| q.disk == part.disk && !q.delete && q.size_mb > 0)
|
||||
.map(|q| q.size_mb)
|
||||
.sum();
|
||||
let cap = state.available_disks.iter()
|
||||
.find(|dk| dk.name == part.disk)
|
||||
.map(|dk| dk.size_bytes / 1_048_576)
|
||||
.unwrap_or(0);
|
||||
let rem = cap.saturating_sub(others);
|
||||
if rem >= 1024 {
|
||||
format!("{:.1} GB", rem as f64 / 1024.0)
|
||||
} else {
|
||||
format!("{} MB", rem)
|
||||
}
|
||||
} else if part.size_mb >= 1024 {
|
||||
format!("{:.1} GB", part.size_mb as f64 / 1024.0)
|
||||
} else {
|
||||
format!("{} MB", part.size_mb)
|
||||
};
|
||||
if part.delete {
|
||||
plan_lines.push(format!("{} \u{EA6B} {} ({}, {}) \u{ECA1} {}",
|
||||
part.device, "—", "—", size_str, t!("mp_will_delete")));
|
||||
} else {
|
||||
plan_lines.push(format!("{} \u{EA6B} {} ({}, {})",
|
||||
part.device, part.mountpoint, part.fstype, size_str));
|
||||
}
|
||||
}
|
||||
}
|
||||
summary_row(ui, t!("summary_custom_partitions"), &plan_lines.join("\n"));
|
||||
}
|
||||
|
||||
+133
-16
@@ -52,6 +52,8 @@ pub fn c_sidebar_text() -> Color32 { color_from_hex(SIDEBAR_TEXT.load(Ordering::
|
||||
pub fn c_footer_bg() -> Color32 { color_from_hex(BG_FOOTER.load(Ordering::Relaxed)) }
|
||||
pub fn c_root_bg() -> Color32 { color_from_hex(BG_ROOT.load(Ordering::Relaxed)) }
|
||||
pub fn c_keyboard_bg() -> Color32 { color_from_hex(KEYBOARD_BG.load(Ordering::Relaxed)) }
|
||||
//pub fn select_disk_label() -> Color32 { color_from_hex(0xFFFFFF) }
|
||||
|
||||
|
||||
pub fn set_theme_mode(is_dark: bool) {
|
||||
if is_dark {
|
||||
@@ -108,10 +110,85 @@ pub fn apply(ctx: &egui::Context, is_dark: bool) {
|
||||
ctx.set_visuals(build_visuals(is_dark));
|
||||
}
|
||||
|
||||
// fn setup_fonts(ctx: &egui::Context) {
|
||||
// let mut fonts = egui::FontDefinitions::default();
|
||||
|
||||
// // 1. ADIM: Sadece Metin (Text) Fontunu Bul ve Yükle
|
||||
// let text_font_paths = [
|
||||
// "/usr/share/fonts/TTF/OpenSans-Regular.ttf",
|
||||
// "/usr/share/fonts/noto/NotoSans-Regular.ttf",
|
||||
// "/usr/share/fonts/exo-1/Exo-Regular.otf",
|
||||
// "/usr/share/fonts/exo-1/Exo2-Medium.ttf",
|
||||
// "/usr/share/fonts/exo-1/Exo-Medium.otf",
|
||||
// ];
|
||||
|
||||
// for path in text_font_paths {
|
||||
// if let Ok(data) = std::fs::read(path) {
|
||||
// fonts.font_data.insert(
|
||||
// "system_text_font".to_owned(),
|
||||
// egui::FontData::from_owned(data),
|
||||
// );
|
||||
|
||||
// // Metin fontunu en başa (0. indeks) koyuyoruz ki varsayılan olarak bu kullanılsın
|
||||
// fonts
|
||||
// .families
|
||||
// .entry(egui::FontFamily::Proportional)
|
||||
// .or_default()
|
||||
// .insert(0, "system_text_font".to_owned());
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
|
||||
// // 2. ADIM: Renkli Emoji Fontunu Ara ve "Fallback" (Yedek) Olarak Sona Ekle
|
||||
// // ISO ortamlarında Noto Color Emoji genellikle bu yollardan birinde olur
|
||||
// let emoji_font_paths = [
|
||||
// "/usr/share/fonts/noto/NotoColorEmoji.ttf",
|
||||
// ];
|
||||
|
||||
// for path in emoji_font_paths {
|
||||
// if let Ok(data) = std::fs::read(path) {
|
||||
// fonts.font_data.insert(
|
||||
// "system_emoji_font".to_owned(),
|
||||
// egui::FontData::from_owned(data),
|
||||
// );
|
||||
|
||||
// // ÖNEMLİ: Emojiyi listenin SONUNA (push ile) ekliyoruz.
|
||||
// // Böylece egui normal harfleri sistem fontuyla yazacak,
|
||||
// // harf sistem fontunda yoksa (yani emojiyse) gidip bu fonttan alacak.
|
||||
// fonts
|
||||
// .families
|
||||
// .entry(egui::FontFamily::Proportional)
|
||||
// .or_default()
|
||||
// .push("system_emoji_font".to_owned());
|
||||
|
||||
// fonts
|
||||
// .families
|
||||
// .entry(egui::FontFamily::Monospace)
|
||||
// .or_default()
|
||||
// .insert(0, "system_emoji_font".to_owned());
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
|
||||
// // 3. ADIM: Remixicon ikon fontunu projeye göm (PUA bölgesindeki karakterler için fallback)
|
||||
// let remixicon_data = include_bytes!("../../assets/remixicon.ttf");
|
||||
// fonts.font_data.insert(
|
||||
// "remixicon".to_owned(),
|
||||
// egui::FontData::from_owned(remixicon_data.to_vec()),
|
||||
// );
|
||||
// fonts
|
||||
// .families
|
||||
// .entry(egui::FontFamily::Proportional)
|
||||
// .or_default()
|
||||
// .push("remixicon".to_owned());
|
||||
|
||||
// ctx.set_fonts(fonts);
|
||||
// }
|
||||
|
||||
fn setup_fonts(ctx: &egui::Context) {
|
||||
let mut fonts = egui::FontDefinitions::default();
|
||||
|
||||
// 1. ADIM: Sadece Metin (Text) Fontunu Bul ve Yükle
|
||||
// 1. ADIM: Metin ve Monospace için Sistem Fontu Yükleme
|
||||
let text_font_paths = [
|
||||
"/usr/share/fonts/TTF/OpenSans-Regular.ttf",
|
||||
"/usr/share/fonts/noto/NotoSans-Regular.ttf",
|
||||
@@ -120,25 +197,59 @@ fn setup_fonts(ctx: &egui::Context) {
|
||||
"/usr/share/fonts/exo-1/Exo-Medium.otf",
|
||||
];
|
||||
|
||||
let mut _text_font_loaded = false;
|
||||
|
||||
for path in text_font_paths {
|
||||
if let Ok(data) = std::fs::read(path) {
|
||||
fonts.font_data.insert(
|
||||
"system_text_font".to_owned(),
|
||||
egui::FontData::from_owned(data),
|
||||
egui::FontData::from_owned(data).into(), // Sürüme göre .into() veya std::sync::Arc::new(...)
|
||||
);
|
||||
|
||||
// Metin fontunu en başa (0. indeks) koyuyoruz ki varsayılan olarak bu kullanılsın
|
||||
|
||||
// Proportional (Orantılı) fontun başına ekle
|
||||
fonts
|
||||
.families
|
||||
.entry(egui::FontFamily::Proportional)
|
||||
.or_default()
|
||||
.insert(0, "system_text_font".to_owned());
|
||||
|
||||
// Monospace için de bu fontu varsayılan olarak ekle (özel bir monospace bulunamadıysa)
|
||||
fonts
|
||||
.families
|
||||
.entry(egui::FontFamily::Monospace)
|
||||
.or_default()
|
||||
.insert(0, "system_text_font".to_owned());
|
||||
|
||||
_text_font_loaded = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 2. ADIM: Renkli Emoji Fontunu Ara ve "Fallback" (Yedek) Olarak Sona Ekle
|
||||
// ISO ortamlarında Noto Color Emoji genellikle bu yollardan birinde olur
|
||||
// Ek Güvence: Özel bir Monospace fontu varsa öncelikli olarak onu ara
|
||||
let mono_font_paths = [
|
||||
"/usr/share/fonts/TTF/Hack-Regular.ttf",
|
||||
"/usr/share/fonts/TTF/DejaVuSansMono.ttf",
|
||||
"/usr/share/fonts/noto/NotoSansMono-Regular.ttf",
|
||||
];
|
||||
|
||||
for path in mono_font_paths {
|
||||
if let Ok(data) = std::fs::read(path) {
|
||||
fonts.font_data.insert(
|
||||
"system_mono_font".to_owned(),
|
||||
egui::FontData::from_owned(data).into(),
|
||||
);
|
||||
|
||||
// Monospace ailesinin 0. indeksine yerleştir
|
||||
fonts
|
||||
.families
|
||||
.entry(egui::FontFamily::Monospace)
|
||||
.or_default()
|
||||
.insert(0, "system_mono_font".to_owned());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 2. ADIM: Color Emoji Fontunu Yedek (Fallback) Olarak EKLE
|
||||
let emoji_font_paths = [
|
||||
"/usr/share/fonts/noto/NotoColorEmoji.ttf",
|
||||
];
|
||||
@@ -147,39 +258,44 @@ fn setup_fonts(ctx: &egui::Context) {
|
||||
if let Ok(data) = std::fs::read(path) {
|
||||
fonts.font_data.insert(
|
||||
"system_emoji_font".to_owned(),
|
||||
egui::FontData::from_owned(data),
|
||||
egui::FontData::from_owned(data).into(),
|
||||
);
|
||||
|
||||
// ÖNEMLİ: Emojiyi listenin SONUNA (push ile) ekliyoruz.
|
||||
// Böylece egui normal harfleri sistem fontuyla yazacak,
|
||||
// harf sistem fontunda yoksa (yani emojiyse) gidip bu fonttan alacak.
|
||||
// Her iki aileye de SONA (push) ekle, böylece metinler bozulmaz
|
||||
fonts
|
||||
.families
|
||||
.entry(egui::FontFamily::Proportional)
|
||||
.or_default()
|
||||
.push("system_emoji_font".to_owned());
|
||||
|
||||
|
||||
fonts
|
||||
.families
|
||||
.entry(egui::FontFamily::Monospace)
|
||||
.or_default()
|
||||
.insert(0, "system_emoji_font".to_owned());
|
||||
.push("system_emoji_font".to_owned()); // insert(0, ...) yerine push yapıldı!
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 3. ADIM: Remixicon ikon fontunu projeye göm (PUA bölgesindeki karakterler için fallback)
|
||||
// 3. ADIM: Remixicon İkon Fontunu Göm (PUA)
|
||||
let remixicon_data = include_bytes!("../../assets/remixicon.ttf");
|
||||
fonts.font_data.insert(
|
||||
"remixicon".to_owned(),
|
||||
egui::FontData::from_owned(remixicon_data.to_vec()),
|
||||
egui::FontData::from_owned(remixicon_data.to_vec()).into(),
|
||||
);
|
||||
|
||||
fonts
|
||||
.families
|
||||
.entry(egui::FontFamily::Proportional)
|
||||
.or_default()
|
||||
.push("remixicon".to_owned());
|
||||
|
||||
fonts
|
||||
.families
|
||||
.entry(egui::FontFamily::Monospace)
|
||||
.or_default()
|
||||
.push("remixicon".to_owned());
|
||||
|
||||
ctx.set_fonts(fonts);
|
||||
}
|
||||
|
||||
@@ -187,7 +303,7 @@ fn setup_fonts(ctx: &egui::Context) {
|
||||
|
||||
fn build_visuals(is_dark: bool) -> Visuals {
|
||||
let mut v = if is_dark { Visuals::dark() } else { Visuals::light() };
|
||||
|
||||
|
||||
// Genel arka planlar
|
||||
v.window_fill = c_bg_dark();
|
||||
v.panel_fill = c_bg_panel();
|
||||
@@ -243,13 +359,14 @@ fn build_visuals(is_dark: bool) -> Visuals {
|
||||
|
||||
fn build_style() -> egui::Style {
|
||||
let mut style = egui::Style::default();
|
||||
|
||||
|
||||
// Genel boşluk ve boyutlar
|
||||
style.spacing.item_spacing = Vec2::new(8.0, 6.0);
|
||||
style.spacing.button_padding = Vec2::new(12.0, 6.0);
|
||||
style.spacing.indent = 16.0;
|
||||
style.spacing.scroll.bar_width = 14.0;
|
||||
|
||||
|
||||
// Metin boyutları
|
||||
style.text_styles = [
|
||||
(egui::TextStyle::Small, FontId::proportional(11.0)),
|
||||
|
||||
Reference in New Issue
Block a user