diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6261196..13f8dd6 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -43,6 +43,19 @@
---
+## [4.0.2] — 2026-06-21
+
+### Changed
+- Sürgü maksimum değeri artık diskteki kullanılabilir alan kadar (diğer bölümler hariç)
+- Düzenleme modunda seçili bölümün kapasitesi sürgü aralığına dahil edildi
+- `form_input_text` boyut alanı doğrudan `add_size_str`'e bağlandı, yanında formatlı MB gösterimi eklendi
+- Boyut etiketi artık `"123 MB"` yerine `"123.456 MB"` formatında (parse edilebilen değerlerde)
+
+### Fixed
+- Boyut input alanına yazılan değer `add_size_str`'e geri yazılmıyordu — text edit salt okunur kalıyordu
+
+---
+
## [4.0.1] — 2026-06-19
### Added
diff --git a/assets/icons/fail-icon.svg b/assets/icons/fail-icon.svg
new file mode 100644
index 0000000..3e337e6
--- /dev/null
+++ b/assets/icons/fail-icon.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/assets/icons/warning-icon.svg b/assets/icons/warning-icon.svg
new file mode 100644
index 0000000..3061c76
--- /dev/null
+++ b/assets/icons/warning-icon.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/locales/en.toml b/locales/en.toml
index 9fa6037..a10fc3c 100644
--- a/locales/en.toml
+++ b/locales/en.toml
@@ -268,6 +268,9 @@ mp_select_hint = "Select…"
mp_add_btn = "✓ Add"
mp_size_hint = "Size (MB, 0=remaining):"
mp_delete_confirm_yes = "✓ Yes, Delete"
+mp_new_partition = "New Partition"
+mp_edit_partition = "Edit"
+mp_save = "✓ Save"
plan_root_suffix = " (root)"
disk_min_required = " ↳ %{err} (min. %{n} GB required)"
diff --git a/locales/tr.toml b/locales/tr.toml
index c488ced..140a547 100644
--- a/locales/tr.toml
+++ b/locales/tr.toml
@@ -240,6 +240,9 @@ mp_select_hint = "Seç…"
mp_add_btn = "✓ Ekle"
mp_size_hint = "Boyut (MB, 0=kalan):"
mp_delete_confirm_yes = "✓ Evet, Sil"
+mp_new_partition = "Yeni Bölüm"
+mp_edit_partition = "Düzenle"
+mp_save = "✓ Kaydet"
plan_root_suffix = " (kök)"
disk_min_required = " ↳ %{err} (min. %{n} GB gerekli)"
diff --git a/src/installer.rs b/src/installer.rs
index 18ef3d0..60e59ff 100644
--- a/src/installer.rs
+++ b/src/installer.rs
@@ -449,15 +449,31 @@ impl InstallerStep for WelcomeStep {
egui::Grid::new("checks_grid")
.num_columns(3)
.spacing([10.0, 6.0])
- .striped(true)
.show(ui, |ui| {
for check in &self.checks {
- let (icon, color) = match check.status {
- CheckStatus::Pass => ("✅", theme::c_success()),
- CheckStatus::Warn => ("⚠️", theme::c_warning()),
- CheckStatus::Fail => ("❌", theme::c_error()),
- };
- ui.colored_label(color, icon);
+ match check.status {
+ CheckStatus::Pass => {
+ ui.add(
+ egui::Image::new(egui::include_image!("../assets/icons/check.svg"))
+ .max_width(18.0).max_height(18.0)
+ .tint(theme::c_success())
+ );
+ }
+ CheckStatus::Warn => {
+ ui.add(
+ egui::Image::new(egui::include_image!("../assets/icons/warning-icon.svg"))
+ .max_width(18.0).max_height(18.0)
+ .tint(theme::c_warning())
+ );
+ }
+ CheckStatus::Fail => {
+ ui.add(
+ egui::Image::new(egui::include_image!("../assets/icons/fail-icon.svg"))
+ .max_width(18.0).max_height(18.0)
+ .tint(theme::c_error())
+ );
+ }
+ }
ui.label(egui::RichText::new(&check.name).strong().color(theme::c_text()));
ui.label(
egui::RichText::new(&check.message)
diff --git a/src/steps/partition.rs b/src/steps/partition.rs
index 716f3ac..4fd7b8d 100644
--- a/src/steps/partition.rs
+++ b/src/steps/partition.rs
@@ -3,6 +3,7 @@
use eframe::egui;
use rust_i18n::t;
use crate::ui::theme;
+use crate::funct::{form_label, form_input_text};
// sysinfo::Disks mount point başına bir kayıt döndürür (partition'lar),
// ham disk listesi için lsblk kullanıyoruz.
@@ -220,6 +221,12 @@ const COMMON_MOUNTPOINTS: &[&str] = &["/", "/boot", "/boot/efi", "/home", "/var"
struct ManualState {
/// "Bölüm Ekle" diyaloğu açık mı?
adding: bool,
+ /// "Bölüm Düzenle" diyaloğı açık mı?
+ editing: bool,
+ /// Düzenlenecek bölüm indeksi
+ edit_idx: Option,
+ /// Seçili satır indeksi
+ selected_idx: Option,
/// Diyalog: seçili dosya sistemi
add_fstype: usize, // FsType listesindeki indeks
/// Diyalog: boyut alanı (MB, metin girişi)
@@ -363,8 +370,17 @@ impl InstallerStep for PartitionStep {
// ── Mod seçimi ─────────────────────────────────────────
ui.horizontal(|ui| {
- ui.selectable_value(&mut state.erase_disk, true, t!("erase_disk_label"));
- ui.selectable_value(&mut state.erase_disk, false, t!("manual_partition_label"));
+ if state.erase_disk {
+ let _ = theme::primary_button(ui, &t!("erase_disk_label"));
+ if theme::secondary_button(ui, &t!("manual_partition_label")).clicked() {
+ state.erase_disk = false;
+ }
+ } else {
+ if theme::secondary_button(ui, &t!("erase_disk_label")).clicked() {
+ state.erase_disk = true;
+ }
+ let _ = theme::primary_button(ui, &t!("manual_partition_label"));
+ }
});
ui.add_space(10.0);
@@ -415,12 +431,10 @@ impl PartitionStep {
// arka plan
egui::Frame::none()
.fill(if selected {
- //crate::ui::theme::c_accent_dim()
egui::Color32::from_rgba_unmultiplied(237, 237, 224, 1)
} else if response.hovered() {
egui::Color32::from_rgba_unmultiplied(60, 60, 80, 180)
} else {
- //crate::ui::theme::c_bg_widget()
egui::Color32::from_rgba_unmultiplied(237, 237, 224, 1)
})
.stroke(egui::Stroke::new(
@@ -512,7 +526,6 @@ impl PartitionStep {
let child_ui = &mut ui.child_ui(rect, egui::Layout::top_down(egui::Align::LEFT));
egui::Frame::none()
.fill(header_color)
- .stroke(egui::Stroke::new(1.0, header_color))
.inner_margin(egui::Margin::symmetric(8.0, 4.0))
.show(child_ui, |ui| {
ui.label(egui::RichText::new(h)
@@ -629,7 +642,6 @@ impl PartitionStep {
impl PartitionStep {
fn show_manual_mode(&mut self, ui: &mut egui::Ui, state: &mut GlobalState) {
- let accent = crate::ui::theme::c_accent();
let kart_genislik = 180.0;
let kart_yukseklik = 160.0;
@@ -654,12 +666,10 @@ impl PartitionStep {
egui::Frame::none()
.fill(if selected {
- //crate::ui::theme::c_accent_dim()
egui::Color32::from_rgba_unmultiplied(237, 237, 224, 1)
} else if response.hovered() {
egui::Color32::from_rgba_unmultiplied(60, 60, 80, 180)
} else {
- //crate::ui::theme::c_bg_widget()
egui::Color32::from_rgba_unmultiplied(237, 237, 224, 1)
})
.stroke(egui::Stroke::new(
@@ -683,20 +693,20 @@ impl PartitionStep {
} else {
&disk.vendor
};
-
+
ui.label(
egui::RichText::new(marka)
.size(11.0)
.color(crate::ui::theme::c_text_dim()),
);
-
+
ui.label(
egui::RichText::new(format!("{} GB", disk.size_gb))
.size(14.0)
.strong()
.color(crate::ui::theme::c_text()),
);
-
+
});
if response.clicked() {
@@ -721,12 +731,17 @@ impl PartitionStep {
return;
}
};
+ let disk_size_gb = state.available_disks.iter()
+ .find(|d| d.name == disk)
+ .map(|d| d.size_gb)
+ .unwrap_or(256);
+ let max_mb = (disk_size_gb as f64) * 1024.0;
// ── Mevcut bölüm listesi tablosu ───────────────────────
ui.label(egui::RichText::new(t!("mp_plan_title")).strong().color(crate::ui::theme::c_text()));
ui.add_space(6.0);
- let mut delete_idx: Option = None;
+ let delete_idx: Option = None;
let col_widths = [0.28, 0.12, 0.18, 0.27, 0.15];
let tam_genislik = ui.available_width();
@@ -741,12 +756,11 @@ impl PartitionStep {
grid.show(ui, |ui| {
// ── Başlık satırı ─────────────────────────────────
- let headers: [String; 5] = [
+ let headers: [String; 4] = [
t!("mp_device").to_string(),
t!("mp_size").to_string(),
t!("mp_fstype").to_string(),
- t!("mp_mountpoint").to_string(),
- t!("mp_action").to_string(),
+ t!("mp_mountpoint").to_string(),
];
for (j, h) in headers.into_iter().enumerate() {
let (rect, _) = ui.allocate_exact_size(
@@ -766,7 +780,10 @@ impl PartitionStep {
// ── Veri satırları ────────────────────────────────
for (row_idx, part) in state.custom_partitions.iter().enumerate() {
- let row_bg = if row_idx % 2 == 0 {
+ let is_selected = self.manual.selected_idx == Some(row_idx);
+ let row_bg = if is_selected {
+ egui::Color32::from_rgba_unmultiplied(80, 100, 180, 80)
+ } else if row_idx % 2 == 0 {
egui::Color32::from_rgba_unmultiplied(60, 60, 80, 60)
} else {
egui::Color32::TRANSPARENT
@@ -794,22 +811,32 @@ impl PartitionStep {
];
for (j, (deger, is_action)) in hucreler.iter().enumerate() {
- let (rect, _response) = ui.allocate_exact_size(
+ let sense = if *is_action {
+ egui::Sense::click()
+ } else {
+ egui::Sense::click()
+ };
+ let (rect, response) = ui.allocate_exact_size(
egui::vec2(tam_genislik * col_widths[j], 34.0),
- egui::Sense::click(),
+ sense,
);
+
+ if !*is_action {
+ if response.clicked() {
+ self.manual.selected_idx = Some(row_idx);
+ }
+ response.on_hover_cursor(egui::CursorIcon::PointingHand);
+ }
+
let child_ui = &mut ui.child_ui(rect, egui::Layout::top_down(egui::Align::Center));
if *is_action {
egui::Frame::none()
- .fill(row_bg)
- .stroke(egui::Stroke::new(0.0, egui::Color32::TRANSPARENT))
+ .fill(egui::Color32::TRANSPARENT)
.inner_margin(egui::Margin::symmetric(8.0, 4.0))
.show(child_ui, |ui| {
ui.vertical_centered(|ui| {
- if ui.small_button("🗑").on_hover_text(t!("mp_delete")).clicked() {
- delete_idx = Some(row_idx);
- }
+ ui.label(egui::RichText::new(deger).size(13.0).color(crate::ui::theme::c_text()));
});
});
} else {
@@ -831,6 +858,16 @@ impl PartitionStep {
}
});
+ // ── Bölümleme Planı (manuel) ──────────────────────────
+ if !state.custom_partitions.is_empty() {
+ ui.add_space(12.0);
+ ui.separator();
+ ui.add_space(8.0);
+ ui.label(egui::RichText::new(t!("partition_plan_title")).strong().color(crate::ui::theme::c_text()));
+ ui.add_space(6.0);
+ manual_partition_bar(ui, &state.custom_partitions);
+ }
+
// Silme onayı
if let Some(idx) = delete_idx {
self.manual.delete_idx = Some(idx);
@@ -847,13 +884,14 @@ impl PartitionStep {
ui.label(t!("mp_confirm_delete", dev = dev));
ui.add_space(8.0);
ui.horizontal(|ui| {
- if ui.button(t!("mp_delete_confirm_yes")).clicked() {
+ if theme::danger_button(ui, &t!("mp_delete_confirm_yes")).clicked() {
if idx < state.custom_partitions.len() {
state.custom_partitions.remove(idx);
}
self.manual.delete_idx = None;
+ self.manual.selected_idx = None;
}
- if ui.button(t!("cancel")).clicked() {
+ if theme::secondary_button(ui, &t!("cancel")).clicked() {
self.manual.delete_idx = None;
}
});
@@ -862,126 +900,199 @@ impl PartitionStep {
ui.add_space(8.0);
- // ── "Bölüm Ekle" butonu ────────────────────────────────
- let btn = egui::Button::new(format!("➕ {}", t!("mp_add")))
- .fill(accent.gamma_multiply(0.15));
- if ui.add(btn).clicked() {
- self.manual.adding = true;
- let next_num = state.custom_partitions.len() + 1;
- self.manual.add_size_str.clear();
- self.manual.add_mountpoint.clear();
- self.manual.add_fstype = 0;
- // Akıllı varsayılan bağlama noktası
- self.manual.add_mountpoint = match next_num {
- 1 if self.is_uefi => "/boot/efi".to_string(),
- 1 => "/".to_string(),
- 2 if self.is_uefi => "swap".to_string(),
- 2 => "swap".to_string(),
- 3 => "/".to_string(),
- _ => String::new(),
- };
- }
+ // ── Butonlar: Yeni Bölüm / Düzenle / Sil ───────────
+ ui.horizontal(|ui| {
+ let has_selection = self.manual.selected_idx.is_some();
+ let selected = self.manual.selected_idx
+ .and_then(|i| state.custom_partitions.get(i));
- // ── "Bölüm Ekle" diyaloğu ──────────────────────────────
- if self.manual.adding {
- egui::Window::new(t!("mp_add"))
+ // Yeni Bölüm
+ if theme::primary_button(ui, &format!("➕ {}", t!("mp_new_partition"))).clicked() {
+ self.manual.adding = true;
+ self.manual.editing = false;
+ self.manual.edit_idx = None;
+ let next_num = state.custom_partitions.len() + 1;
+ self.manual.add_size_str.clear();
+ self.manual.add_mountpoint.clear();
+ self.manual.add_fstype = 0;
+ self.manual.add_encrypt = false;
+ self.manual.add_luks_password.clear();
+ self.manual.add_mountpoint = match next_num {
+ 1 if self.is_uefi => "/boot/efi".to_string(),
+ 1 => "/".to_string(),
+ 2 if self.is_uefi => "swap".to_string(),
+ 2 => "swap".to_string(),
+ 3 => "/".to_string(),
+ _ => String::new(),
+ };
+ }
+
+ // Düzenle
+ ui.add_enabled_ui(has_selection, |ui| {
+ if theme::secondary_button(ui, &format!("✏️ {}", t!("mp_edit_partition"))).clicked() {
+ if let Some(part) = selected {
+ self.manual.editing = true;
+ self.manual.adding = false;
+ self.manual.edit_idx = self.manual.selected_idx;
+ self.manual.add_size_str = if part.size_mb == 0 {
+ String::new()
+ } else {
+ part.size_mb.to_string()
+ };
+ self.manual.add_mountpoint = part.mountpoint.clone();
+ self.manual.add_fstype = FS_OPTIONS.iter()
+ .position(|(_, ft)| *ft == part.fstype)
+ .unwrap_or(0);
+ self.manual.add_encrypt = part.encrypt;
+ self.manual.add_luks_password = part.luks_password.clone();
+ }
+ }
+ });
+
+ // Sil
+ ui.add_enabled_ui(has_selection, |ui| {
+ if theme::danger_button(ui, &format!("🗑 {}", t!("mp_delete"))).clicked() {
+ self.manual.delete_idx = self.manual.selected_idx;
+ }
+ });
+ });
+
+ // ── Bölüm Ekle / Düzenle diyaloğu ─────────────────
+ let dialog_open = self.manual.adding || self.manual.editing;
+ if dialog_open {
+ let is_edit = self.manual.editing;
+ let title = if is_edit { t!("mp_edit_partition") } else { t!("mp_new_partition") };
+ egui::Window::new(title)
.collapsible(false)
.resizable(false)
.anchor(egui::Align2::CENTER_CENTER, [0.0, 0.0])
.show(ui.ctx(), |ui| {
- egui::Grid::new("add_part_grid")
- .num_columns(2)
- .spacing([12.0, 8.0])
- .show(ui, |ui| {
-
- // Dosya sistemi (boyut önerisi ile)
- ui.label(t!("mp_fstype"));
- let prev_fs = self.manual.add_fstype;
- let fs_label = FS_OPTIONS.get(self.manual.add_fstype).map(|x| x.0).unwrap_or("?");
- theme::show_combo_box(ui, "fs_combo", fs_label, ui.available_width(), |ui, w| {
- for (i, (label, _)) in FS_OPTIONS.iter().enumerate() {
- theme::selectable_value(ui, &mut self.manual.add_fstype, i, *label, w);
- }
- });
- // FS tipi değişince akıllı boyut öner
- if prev_fs != self.manual.add_fstype && self.manual.add_size_str.is_empty() {
- let fs = FS_OPTIONS.get(self.manual.add_fstype).map(|x| &x.1);
- match fs {
- Some(FsType::Fat32) => self.manual.add_size_str = "300".to_string(),
- Some(FsType::Swap) => self.manual.add_size_str = format!("{}", recommended_swap_mb()),
- _ => {}
- }
- }
- ui.end_row();
-
- // Boyut
- ui.label(t!("mp_size_hint"));
- ui.text_edit_singleline(&mut self.manual.add_size_str);
- ui.end_row();
-
- // Bağlama noktası (2 aşamalı: combo + input)
- ui.label(t!("mp_mountpoint"));
- ui.horizontal(|ui| {
- let current_mp = if self.manual.add_mountpoint.is_empty() {
- t!("mp_select_hint").to_string()
- } else {
- self.manual.add_mountpoint.clone()
- };
- theme::show_combo_box(ui, "mp_combo", ¤t_mp, 120.0, |ui, w| {
- for mp in COMMON_MOUNTPOINTS {
- theme::selectable_value(ui, &mut self.manual.add_mountpoint, mp.to_string(), *mp, w);
- }
- });
- let mp_field = egui::TextEdit::singleline(&mut self.manual.add_mountpoint)
- .hint_text(t!("mp_add_dialog_hint"));
- ui.add(mp_field);
- });
- ui.end_row();
-
- // LUKS şifreleme
- ui.label(t!("luks_enable_root"));
- let fstype = FS_OPTIONS.get(self.manual.add_fstype).map(|x| x.1.clone()).unwrap_or(FsType::Ext4);
- let can_encrypt = matches!(fstype, FsType::Ext4 | FsType::Ext3 | FsType::Btrfs | FsType::Xfs);
- ui.add_enabled_ui(can_encrypt, |ui| {
- ui.checkbox(&mut self.manual.add_encrypt, "");
- });
- if !can_encrypt { self.manual.add_encrypt = false; }
- ui.end_row();
-
- if self.manual.add_encrypt {
- ui.label(t!("luks_password"));
- let pw = &mut self.manual.add_luks_password;
- ui.horizontal(|ui| {
- if self.show_luks_password {
- ui.text_edit_singleline(pw);
- } else {
- ui.add(egui::TextEdit::singleline(pw).password(true));
- }
- let eye = if self.show_luks_password { "🙈" } else { "👁" };
- if ui.small_button(eye).clicked() {
- self.show_luks_password = !self.show_luks_password;
- }
- });
- ui.end_row();
+ ui.label(t!("mp_fstype"));
+ let prev_fs = self.manual.add_fstype;
+ let fs_label = FS_OPTIONS.get(self.manual.add_fstype).map(|x| x.0).unwrap_or("?");
+ theme::show_combo_box(ui, "fs_combo", fs_label, ui.available_width(), |ui, w| {
+ for (i, (label, _)) in FS_OPTIONS.iter().enumerate() {
+ theme::selectable_value(ui, &mut self.manual.add_fstype, i, *label, w);
}
});
+ // FS tipi değişince akıllı boyut öner
+ if !is_edit && prev_fs != self.manual.add_fstype && self.manual.add_size_str.is_empty() {
+ let fs = FS_OPTIONS.get(self.manual.add_fstype).map(|x| &x.1);
+ match fs {
+ Some(FsType::Fat32) => self.manual.add_size_str = "300".to_string(),
+ Some(FsType::Swap) => self.manual.add_size_str = format!("{}", recommended_swap_mb()),
+ _ => {}
+ }
+ }
+
+ ui.add_space(6.0);
+ form_label(ui, &t!("mp_size_hint"));
+ ui.horizontal(|ui| {
+ form_input_text(ui, &mut self.manual.add_size_str);
+ let parsed: u64 = self.manual.add_size_str.trim().parse().unwrap_or(0);
+ if parsed > 0 {
+ ui.label(format!("{} MB", format_number_thousands(parsed)));
+ } else {
+ ui.label("MB");
+ }
+ });
+ // Slider — kullanılabilir alan kadar
+ let used_mb: u64 = state.custom_partitions.iter()
+ .enumerate()
+ .filter(|(i, p)| p.size_mb > 0 && Some(*i) != self.manual.edit_idx)
+ .map(|(_, p)| p.size_mb)
+ .sum();
+ let avail_mb = (max_mb as u64).saturating_sub(used_mb).max(1);
+ let mut size_val = self.manual.add_size_str.trim().parse::().unwrap_or(0.0);
+ ui.add_space(2.0);
+ if ui.add(egui::Slider::new(&mut size_val, 0.0..=avail_mb as f64)
+ .show_value(false)
+ .max_decimals(0)
+ ).changed() {
+ if size_val == 0.0 {
+ self.manual.add_size_str = "0".to_string();
+ } else {
+ self.manual.add_size_str = format!("{}", size_val as u64);
+ }
+ }
+
+ ui.add_space(6.0);
+ ui.label(t!("mp_mountpoint"));
+ ui.horizontal(|ui| {
+ let current_mp = if self.manual.add_mountpoint.is_empty() {
+ t!("mp_select_hint").to_string()
+ } else {
+ self.manual.add_mountpoint.clone()
+ };
+ theme::show_combo_box(ui, "mp_combo", ¤t_mp, 120.0, |ui, w| {
+ for mp in COMMON_MOUNTPOINTS {
+ theme::selectable_value(ui, &mut self.manual.add_mountpoint, mp.to_string(), *mp, w);
+ }
+ });
+ form_input_text(ui, &mut self.manual.add_mountpoint);
+ });
+
+ ui.add_space(6.0);
+ ui.label(t!("luks_enable_root"));
+ let fstype = FS_OPTIONS.get(self.manual.add_fstype).map(|x| x.1.clone()).unwrap_or(FsType::Ext4);
+ let can_encrypt = matches!(fstype, FsType::Ext4 | FsType::Ext3 | FsType::Btrfs | FsType::Xfs);
+ ui.add_enabled_ui(can_encrypt, |ui| {
+ ui.checkbox(&mut self.manual.add_encrypt, "");
+ });
+ if !can_encrypt { self.manual.add_encrypt = false; }
+
+ if self.manual.add_encrypt {
+ ui.add_space(6.0);
+ ui.label(t!("luks_password"));
+ let pw = &mut self.manual.add_luks_password;
+ ui.horizontal(|ui| {
+ if self.show_luks_password {
+ ui.text_edit_singleline(pw);
+ } else {
+ ui.add(egui::TextEdit::singleline(pw).password(true));
+ }
+ let eye = if self.show_luks_password { "🙈" } else { "👁" };
+ if ui.small_button(eye).clicked() {
+ self.show_luks_password = !self.show_luks_password;
+ }
+ });
+ }
ui.add_space(8.0);
ui.horizontal(|ui| {
- let ok_btn = egui::Button::new(t!("mp_add_btn")).fill(accent.gamma_multiply(0.2));
- if ui.add(ok_btn).clicked() {
+ let ok_label = if is_edit { t!("mp_save") } else { t!("mp_add_btn") };
+ if theme::primary_button(ui, &ok_label).clicked() {
let size_mb: u64 = self.manual.add_size_str
.trim().parse().unwrap_or(0);
let mp = self.manual.add_mountpoint.trim().to_string();
let fstype = FS_OPTIONS.get(self.manual.add_fstype).map(|x| x.1.clone()).unwrap_or(FsType::Ext4);
- let part_num = state.custom_partitions.len() + 1;
- let device = crate::installer::part_path(&disk, part_num as u32);
+ let encrypt = self.manual.add_encrypt;
+ let luks_password = if encrypt { self.manual.add_luks_password.clone() } else { String::new() };
if mp.is_empty() {
self.manual.error = Some(t!("mp_error_empty_mountpoint").to_string());
+ } else if is_edit {
+ if let Some(idx) = self.manual.edit_idx {
+ if idx < state.custom_partitions.len() {
+ let device = state.custom_partitions[idx].device.clone();
+ state.custom_partitions[idx] = CustomPartition {
+ device,
+ size_mb,
+ fstype,
+ mountpoint: mp,
+ encrypt,
+ luks_password,
+ luks_name: String::new(),
+ };
+ }
+ }
+ self.manual.editing = false;
+ self.manual.edit_idx = None;
+ self.manual.selected_idx = None;
+ self.manual.error = None;
} else {
- let encrypt = self.manual.add_encrypt;
- let luks_password = if encrypt { self.manual.add_luks_password.clone() } else { String::new() };
+ let part_num = state.custom_partitions.len() + 1;
+ let device = crate::installer::part_path(&disk, part_num as u32);
state.custom_partitions.push(CustomPartition {
device,
size_mb,
@@ -997,8 +1108,10 @@ impl PartitionStep {
self.manual.adding = false;
}
}
- if ui.button(t!("cancel")).clicked() {
+ if theme::secondary_button(ui, &t!("cancel")).clicked() {
self.manual.adding = false;
+ self.manual.editing = false;
+ self.manual.edit_idx = None;
}
});
});
@@ -1111,9 +1224,7 @@ impl PartitionStep {
ui.add_space(8.0);
// "Logical Volume Ekle" butonu
- let lv_btn = egui::Button::new(format!("➕ {}", t!("lvm_lv_add")))
- .fill(accent.gamma_multiply(0.15));
- if ui.add(lv_btn).clicked() {
+ if theme::primary_button(ui, &format!("➕ {}", t!("lvm_lv_add"))).clicked() {
self.manual.adding_lv = true;
self.manual.add_lv_name.clear();
self.manual.add_lv_vg_idx = 0;
@@ -1189,8 +1300,7 @@ impl PartitionStep {
ui.add_space(8.0);
ui.horizontal(|ui| {
- let ok_btn = egui::Button::new(t!("mp_add_btn")).fill(accent.gamma_multiply(0.2));
- if ui.add(ok_btn).clicked() {
+ if theme::primary_button(ui, &t!("mp_add_btn")).clicked() {
let size_mb: u64 = self.manual.add_lv_size_str.trim().parse().unwrap_or(0);
let mp = self.manual.add_lv_mountpoint.trim().to_string();
let lv_name = self.manual.add_lv_name.trim().to_string();
@@ -1223,7 +1333,7 @@ impl PartitionStep {
self.manual.error = Some(t!("lvm_lv_error_vg").to_string());
}
}
- if ui.button(t!("cancel")).clicked() {
+ if theme::secondary_button(ui, &t!("cancel")).clicked() {
self.manual.adding_lv = false;
}
});
@@ -1249,7 +1359,84 @@ impl PartitionStep {
}
// ─────────────────────────────────────────────
-// Yardımcı: renkli bölüm şeridi
+// Yardımcı: renkli bölüm şeridi (manuel)
+// ─────────────────────────────────────────────
+
+fn manual_partition_bar(ui: &mut egui::Ui, parts: &[CustomPartition]) {
+ let bar_height = 28.0;
+ let (rect, _) = ui.allocate_exact_size(
+ egui::vec2(ui.available_width(), bar_height),
+ egui::Sense::hover(),
+ );
+ let painter = ui.painter();
+
+ if parts.is_empty() {
+ return;
+ }
+
+ // Each partition gets at least MIN_VISIBLE of the bar so all are visible
+ let sized_total: f32 = parts.iter().map(|p| p.size_mb as f32).sum();
+ let min_visible = 0.04;
+
+ let mut raw: Vec = parts.iter().map(|p| {
+ if sized_total > 0.0 {
+ (p.size_mb as f32 / sized_total).max(min_visible)
+ } else {
+ min_visible
+ }
+ }).collect();
+
+ // Normalize to sum 1.0
+ let sum: f32 = raw.iter().sum();
+ for v in raw.iter_mut() {
+ *v /= sum;
+ }
+
+ let mut segments: Vec<(&str, f32, egui::Color32)> = Vec::new();
+
+ for (i, part) in parts.iter().enumerate() {
+ let label = part.mountpoint.as_str();
+ let color = if part.fstype == FsType::Fat32 {
+ egui::Color32::from_rgb(80, 160, 220)
+ } else if part.fstype == FsType::Swap {
+ egui::Color32::from_rgb(220, 160, 40)
+ } else if part.mountpoint == "/" {
+ egui::Color32::from_rgb(80, 190, 100)
+ } else {
+ egui::Color32::from_rgb(160, 130, 200)
+ };
+ segments.push((label, raw[i], color));
+ }
+
+ let mut x = rect.left();
+ let rounding = egui::Rounding::same(4.0);
+ for (i, (label, frac, color)) in segments.iter().enumerate() {
+ let w = frac * rect.width();
+ let seg_rect = egui::Rect::from_min_size(
+ egui::pos2(x, rect.top()),
+ egui::vec2(w - 1.0, bar_height),
+ );
+ let seg_rounding = if i == 0 {
+ egui::Rounding { nw: rounding.nw, sw: rounding.sw, ne: 0.0, se: 0.0 }
+ } else if i == segments.len() - 1 {
+ egui::Rounding { nw: 0.0, sw: 0.0, ne: rounding.ne, se: rounding.se }
+ } else {
+ egui::Rounding::ZERO
+ };
+ painter.rect_filled(seg_rect, seg_rounding, *color);
+ painter.text(
+ seg_rect.center(),
+ egui::Align2::CENTER_CENTER,
+ *label,
+ egui::FontId::proportional(12.0),
+ egui::Color32::WHITE,
+ );
+ x += w;
+ }
+}
+
+// ─────────────────────────────────────────────
+// Yardımcı: renkli bölüm şeridi (otomatik)
// ─────────────────────────────────────────────
fn partition_bar(ui: &mut egui::Ui, plan: &PartitionPlan) {
@@ -1308,3 +1495,16 @@ fn partition_bar(ui: &mut egui::Ui, plan: &PartitionPlan) {
x += w;
}
}
+
+/// Sayıyı nokta ile binlik ayraçlı formata çevirir (örn. 7000000 → 7.000.000).
+fn format_number_thousands(n: u64) -> String {
+ let s = n.to_string();
+ let mut result = String::new();
+ for (i, c) in s.chars().enumerate() {
+ if i > 0 && (s.len() - i) % 3 == 0 {
+ result.push('.');
+ }
+ result.push(c);
+ }
+ result
+}
diff --git a/src/ui/theme.rs b/src/ui/theme.rs
index 504d741..b52802e 100644
--- a/src/ui/theme.rs
+++ b/src/ui/theme.rs
@@ -272,7 +272,9 @@ pub fn error_box(ui: &mut egui::Ui, msg: &str) {
.inner_margin(egui::Margin::same(10.0))
.show(ui, |ui| {
ui.horizontal(|ui| {
- ui.colored_label(c_error(), "❌");
+ ui.add(egui::Image::new(egui::include_image!("../../assets/icons/fail-icon.svg"))
+ .max_width(18.0).max_height(18.0)
+ .tint(c_error()));
ui.colored_label(c_error(), msg);
});
});
@@ -287,7 +289,9 @@ pub fn warning_box(ui: &mut egui::Ui, msg: &str) {
.inner_margin(egui::Margin::same(10.0))
.show(ui, |ui| {
ui.horizontal(|ui| {
- ui.colored_label(c_warning(), "⚠️");
+ ui.add(egui::Image::new(egui::include_image!("../../assets/icons/warning-icon.svg"))
+ .max_width(18.0).max_height(18.0)
+ .tint(c_warning()));
ui.colored_label(c_warning(), msg);
});
});
@@ -302,7 +306,9 @@ pub fn success_box(ui: &mut egui::Ui, msg: &str) {
.inner_margin(egui::Margin::same(10.0))
.show(ui, |ui| {
ui.horizontal(|ui| {
- ui.colored_label(c_success(), "✅");
+ ui.add(egui::Image::new(egui::include_image!("../../assets/icons/check.svg"))
+ .max_width(18.0).max_height(18.0)
+ .tint(c_success()));
ui.colored_label(c_success(), msg);
});
});