mirror of
https://gitlab.com/erkanisik/yali-rs.git
synced 2026-07-31 03:09:00 +00:00
Merge branch 'main' into 'main'
feat: add hostname sanitization function and integrate it into user step processing See merge request ayhanyalcinsoy/yali-rs!1
This commit is contained in:
@@ -4,7 +4,7 @@
|
|||||||
# ───────────────────────────────────────────────────────────────────────────
|
# ───────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
BINARY := yali-rs
|
BINARY := yali-rs
|
||||||
VERSION := 4.0.0
|
VERSION := 4.0.1
|
||||||
|
|
||||||
# Kurulum yolları (DESTDIR ile paket yöneticisi uyumlu)
|
# Kurulum yolları (DESTDIR ile paket yöneticisi uyumlu)
|
||||||
PREFIX ?= /usr
|
PREFIX ?= /usr
|
||||||
|
|||||||
@@ -25,8 +25,14 @@ keyboard = "Keyboard"
|
|||||||
keyboard_title = "Keyboard Layout"
|
keyboard_title = "Keyboard Layout"
|
||||||
keyboard_description = "Select your keyboard layout and test it below."
|
keyboard_description = "Select your keyboard layout and test it below."
|
||||||
keyboard_layout_label = "Layout"
|
keyboard_layout_label = "Layout"
|
||||||
|
keyboard_variant_label = "Variant"
|
||||||
|
keyboard_layout_search_hint = "Search layouts..."
|
||||||
|
keyboard_layout_search_no_match = "No layouts match"
|
||||||
|
keyboard_variant_search_hint = "Search variants..."
|
||||||
|
keyboard_variant_search_no_match = "No variants match"
|
||||||
keyboard_test_label = "Test Area"
|
keyboard_test_label = "Test Area"
|
||||||
keyboard_test_hint = "Type here to test your keyboard…"
|
keyboard_test_hint = "Type here to test your keyboard…"
|
||||||
|
keyboard_variant_default = "Default"
|
||||||
keyboard_selected = "Selected"
|
keyboard_selected = "Selected"
|
||||||
|
|
||||||
# ── Partition ─────────────────────────────────────────────
|
# ── Partition ─────────────────────────────────────────────
|
||||||
|
|||||||
@@ -25,8 +25,14 @@ keyboard = "Klavye"
|
|||||||
keyboard_title = "Klavye Düzeni"
|
keyboard_title = "Klavye Düzeni"
|
||||||
keyboard_description = "Kullanmak istediğiniz klavye düzenini seçin ve aşağıdan test edin."
|
keyboard_description = "Kullanmak istediğiniz klavye düzenini seçin ve aşağıdan test edin."
|
||||||
keyboard_layout_label = "Düzen"
|
keyboard_layout_label = "Düzen"
|
||||||
|
keyboard_variant_label = "Varyant"
|
||||||
|
keyboard_layout_search_hint = "Düzenlerde ara..."
|
||||||
|
keyboard_layout_search_no_match = "Eşleşen düzen yok"
|
||||||
|
keyboard_variant_search_hint = "Varyantlarda ara..."
|
||||||
|
keyboard_variant_search_no_match = "Eşleşen varyant yok"
|
||||||
keyboard_test_label = "Test Alanı"
|
keyboard_test_label = "Test Alanı"
|
||||||
keyboard_test_hint = "Buraya yazarak klavyenizi test edin…"
|
keyboard_test_hint = "Buraya yazarak klavyenizi test edin…"
|
||||||
|
keyboard_variant_default = "Varsayılan"
|
||||||
keyboard_selected = "Seçili"
|
keyboard_selected = "Seçili"
|
||||||
|
|
||||||
# ── Partition ─────────────────────────────────────────────
|
# ── Partition ─────────────────────────────────────────────
|
||||||
|
|||||||
+94
-27
@@ -131,16 +131,31 @@ impl Job for CopyFilesJob {
|
|||||||
|
|
||||||
// --info=progress2 ham çıktı verir; gerçek uygulamada
|
// --info=progress2 ham çıktı verir; gerçek uygulamada
|
||||||
// satır satır parse edilerek ilerleme güncellenebilir.
|
// satır satır parse edilerek ilerleme güncellenebilir.
|
||||||
run_cmd("rsync", &[
|
let mut args: Vec<String> = vec![
|
||||||
"-aAXH",
|
"-aAXH".to_string(),
|
||||||
"--info=progress2",
|
"--info=progress2".to_string(),
|
||||||
"--exclude=/proc",
|
"--exclude=/proc".to_string(),
|
||||||
"--exclude=/sys",
|
"--exclude=/sys".to_string(),
|
||||||
"--exclude=/dev",
|
"--exclude=/dev".to_string(),
|
||||||
"--exclude=/run",
|
"--exclude=/run".to_string(),
|
||||||
&self.source,
|
];
|
||||||
&self.target,
|
|
||||||
]).await?;
|
if let (Ok(source_path), Ok(target_path)) = (
|
||||||
|
std::path::Path::new(&self.source).canonicalize(),
|
||||||
|
std::path::Path::new(&self.target).canonicalize(),
|
||||||
|
) {
|
||||||
|
if let Ok(relative_target) = target_path.strip_prefix(&source_path) {
|
||||||
|
if !relative_target.as_os_str().is_empty() {
|
||||||
|
args.push(format!("--exclude={}", relative_target.display()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
args.push(self.source.clone());
|
||||||
|
args.push(self.target.clone());
|
||||||
|
|
||||||
|
let arg_refs: Vec<&str> = args.iter().map(|s| s.as_str()).collect();
|
||||||
|
run_cmd("rsync", &arg_refs).await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -592,23 +607,75 @@ async fn sync_partition_table(disk: &str) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// ISO ortamında canlı sistemin kaynak dizinini otomatik tespit eder.
|
/// ISO ortamında canlı sistemin kaynak dizinini otomatik tespit eder.
|
||||||
/// Bulunamazsa varsayılan yolu döner.
|
///
|
||||||
|
/// Sırasıyla:
|
||||||
|
/// 1. `/proc/mounts` içinde squashfs/overlay mount noktalarını tara
|
||||||
|
/// 2. Sabit aday listesini dene (her adayda bin/ + etc/ varlığı aranır)
|
||||||
|
/// 3. Hiçbiri bulunamazsa `/` (canlı sistem kökü) kullan — rsync
|
||||||
|
/// zaten /proc /sys /dev /run'u exclude eder.
|
||||||
pub fn detect_source_dir() -> String {
|
pub fn detect_source_dir() -> String {
|
||||||
let candidates = [
|
// 1. /proc/mounts içinde squashfs veya overlay mount noktalarını tara
|
||||||
"/bootmnt", // PisiLinux ISO mount noktası
|
if let Ok(mounts) = std::fs::read_to_string("/proc/mounts") {
|
||||||
"/mnt/cdrom",
|
for line in mounts.lines() {
|
||||||
"/mnt/sqfs",
|
let parts: Vec<&str> = line.split_whitespace().collect();
|
||||||
"/run/livecd/squashfs-root",
|
if parts.len() < 3 { continue; }
|
||||||
"/run/live/medium",
|
let fs_type = parts[2];
|
||||||
"/lib/live/mount/rootfs",
|
let mount_pt = parts[1];
|
||||||
"/mnt/livecd",
|
|
||||||
];
|
match fs_type {
|
||||||
for path in &candidates {
|
"squashfs" => {
|
||||||
if std::path::Path::new(path).exists() {
|
if mount_pt != "/" {
|
||||||
return format!("{}/", path);
|
let p = std::path::Path::new(mount_pt);
|
||||||
|
if p.join("bin").exists() || p.join("usr/bin").exists() {
|
||||||
|
return format!("{}/", mount_pt.trim_end_matches('/'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"overlay" => {
|
||||||
|
// lowerdir parametresini ayıkla
|
||||||
|
let opts = parts.get(3).unwrap_or(&"");
|
||||||
|
for opt in opts.split(',') {
|
||||||
|
if let Some(lower) = opt.strip_prefix("lowerdir=") {
|
||||||
|
for dir in lower.split(':') {
|
||||||
|
let p = std::path::Path::new(dir);
|
||||||
|
if p.exists() && (p.join("bin").exists() || p.join("usr/bin").exists()) {
|
||||||
|
return format!("{}/", dir.trim_end_matches('/'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"/run/livecd/squashfs-root/".to_string()
|
|
||||||
|
// 2. Sabit aday listesi (her adayda bin/ + etc/ kontrolü)
|
||||||
|
let candidates = [
|
||||||
|
"/run/livecd/squashfs-root",
|
||||||
|
"/run/live/rootfs",
|
||||||
|
"/run/live/medium",
|
||||||
|
"/lib/live/mount/rootfs",
|
||||||
|
"/mnt/sqfs",
|
||||||
|
"/mnt/livecd",
|
||||||
|
"/mnt/cdrom",
|
||||||
|
"/bootmnt/live",
|
||||||
|
"/bootmnt/livecd",
|
||||||
|
"/bootmnt",
|
||||||
|
];
|
||||||
|
for path in &candidates {
|
||||||
|
let p = std::path::Path::new(path);
|
||||||
|
if p.exists() {
|
||||||
|
let has_bin = p.join("bin").exists() || p.join("usr/bin").exists();
|
||||||
|
let has_etc = p.join("etc").exists();
|
||||||
|
if has_bin && has_etc {
|
||||||
|
return format!("{}/", path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Hiçbiri bulunamazsa canlı sistem kökü
|
||||||
|
"/".to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Kurulum kuyruğu için ortak yapılandırma.
|
/// Kurulum kuyruğu için ortak yapılandırma.
|
||||||
@@ -1245,7 +1312,7 @@ impl Job for ConfigureLocaleJob {
|
|||||||
let _ = tokio::fs::write(&pisi_locale_path, pisi_locale).await;
|
let _ = tokio::fs::write(&pisi_locale_path, pisi_locale).await;
|
||||||
|
|
||||||
// /etc/locale.gen — satırın başındaki # kaldırılır
|
// /etc/locale.gen — satırın başındaki # kaldırılır
|
||||||
let gen_path = format!("{}/etc/locale.gen", self.mount);
|
let gen_path = format!("{}/etc/locale-gen", self.mount);
|
||||||
ui.log(format!("Güncelleniyor: {}", gen_path));
|
ui.log(format!("Güncelleniyor: {}", gen_path));
|
||||||
let content = tokio::fs::read_to_string(&gen_path).await.unwrap_or_default();
|
let content = tokio::fs::read_to_string(&gen_path).await.unwrap_or_default();
|
||||||
let updated = content
|
let updated = content
|
||||||
@@ -1262,11 +1329,11 @@ impl Job for ConfigureLocaleJob {
|
|||||||
.join("\n");
|
.join("\n");
|
||||||
tokio::fs::write(&gen_path, updated + "\n")
|
tokio::fs::write(&gen_path, updated + "\n")
|
||||||
.await
|
.await
|
||||||
.map_err(|e| format!("/etc/locale.gen yazılamadı: {}", e))?;
|
.map_err(|e| format!("/etc/locale-gen yazılamadı: {}", e))?;
|
||||||
|
|
||||||
// locale-gen çalıştır
|
// locale-gen çalıştır
|
||||||
ui.log("locale-gen çalıştırılıyor…");
|
ui.log("locale-gen çalıştırılıyor…");
|
||||||
run_chroot(&self.mount, &["locale-gen"]).await?;
|
//run_chroot(&self.mount, &["locale-gen"]).await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
+239
-52
@@ -1,29 +1,138 @@
|
|||||||
use eframe::egui;
|
use eframe::egui;
|
||||||
use rust_i18n::t;
|
use rust_i18n::t;
|
||||||
use crate::installer::{GlobalState, InstallerStep};
|
use crate::installer::{GlobalState, InstallerStep};
|
||||||
|
use crate::ui::theme;
|
||||||
|
|
||||||
/// Klavye düzeni ve varyantı seçimi.
|
/// Klavye düzeni ve varyantı seçimi.
|
||||||
/// Gerçek uygulamada mevcut düzenler `localectl list-keymaps`
|
/// Mevcut düzenler `/usr/share/X11/xkb/rules/evdev.lst` dosyasından okunur.
|
||||||
/// veya `/usr/share/X11/xkb/rules/evdev.lst` dosyasından okunabilir.
|
/// Dosya okunamazsa sabit bir temel liste kullanılır.
|
||||||
pub struct KeyboardStep {
|
pub struct KeyboardStep {
|
||||||
test_input: String,
|
test_input: String,
|
||||||
layouts: Vec<(&'static str, &'static str)>, // (kod, gösterim adı)
|
layout_filter: String,
|
||||||
|
variant_filter: String,
|
||||||
|
layouts: Vec<(String, String)>, // (kod, gösterim adı)
|
||||||
|
variants: Vec<(String, String, String)>, // (layout, variant, gösterim adı)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_keyboard_layouts() -> Vec<(String, String)> {
|
||||||
|
vec![
|
||||||
|
("tr".to_string(), "Türkçe (Q)".to_string()),
|
||||||
|
("us".to_string(), "İngilizce (US)".to_string()),
|
||||||
|
("de".to_string(), "Almanca".to_string()),
|
||||||
|
("fr".to_string(), "Fransızca".to_string()),
|
||||||
|
("es".to_string(), "İspanyolca".to_string()),
|
||||||
|
("ru".to_string(), "Rusça".to_string()),
|
||||||
|
("ar".to_string(), "Arapça".to_string()),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_keyboard_variants() -> Vec<(String, String, String)> {
|
||||||
|
vec![
|
||||||
|
("tr".to_string(), "f".to_string(), "Türkçe (F)".to_string()),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_keyboard_layouts() -> (Vec<(String, String)>, Vec<(String, String, String)>) {
|
||||||
|
let path = "/usr/share/X11/xkb/rules/evdev.lst";
|
||||||
|
let contents = std::fs::read_to_string(path).ok();
|
||||||
|
|
||||||
|
let mut layouts = Vec::new();
|
||||||
|
let mut variants = Vec::new();
|
||||||
|
let mut section: Option<&str> = None;
|
||||||
|
|
||||||
|
if let Some(text) = contents {
|
||||||
|
for line in text.lines() {
|
||||||
|
let line = line.trim_end();
|
||||||
|
if line.starts_with('!') {
|
||||||
|
section = if line.starts_with("! layout") {
|
||||||
|
Some("layout")
|
||||||
|
} else if line.starts_with("! variant") {
|
||||||
|
Some("variant")
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let trimmed = line.trim();
|
||||||
|
if trimmed.is_empty() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(section) = section {
|
||||||
|
if let Some((code, desc)) = split_code_desc(trimmed) {
|
||||||
|
match section {
|
||||||
|
"layout" => layouts.push((code.to_string(), desc.to_string())),
|
||||||
|
"variant" => {
|
||||||
|
if let Some((layout, display)) = desc.split_once(':') {
|
||||||
|
variants.push((layout.trim().to_string(), code.to_string(), display.trim().to_string()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if layouts.is_empty() {
|
||||||
|
(default_keyboard_layouts(), default_keyboard_variants())
|
||||||
|
} else {
|
||||||
|
(layouts, variants)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn split_code_desc(line: &str) -> Option<(&str, &str)> {
|
||||||
|
let mut parts = line.splitn(2, char::is_whitespace);
|
||||||
|
let code = parts.next()?.trim();
|
||||||
|
let desc = parts.next()?.trim();
|
||||||
|
if code.is_empty() || desc.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some((code, desc))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn layout_display_name(layout: &str, layouts: &[(String, String)]) -> String {
|
||||||
|
layouts
|
||||||
|
.iter()
|
||||||
|
.find(|(code, _)| code == layout)
|
||||||
|
.map(|(_, display)| display.clone())
|
||||||
|
.unwrap_or_else(|| layout.to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn variant_display_name(
|
||||||
|
layout: &str,
|
||||||
|
variant: &str,
|
||||||
|
variants: &[(String, String, String)],
|
||||||
|
) -> String {
|
||||||
|
variants
|
||||||
|
.iter()
|
||||||
|
.find(|(layout_code, v_code, _)| layout_code == layout && v_code == variant)
|
||||||
|
.map(|(_, _, display)| display.clone())
|
||||||
|
.unwrap_or_else(|| variant.to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn apply_system_keyboard_layout(layout: &str, variant: &str) {
|
||||||
|
if std::env::var("DISPLAY").is_ok() {
|
||||||
|
let mut cmd = std::process::Command::new("setxkbmap");
|
||||||
|
cmd.arg(layout);
|
||||||
|
if !variant.is_empty() {
|
||||||
|
cmd.arg("-variant").arg(variant);
|
||||||
|
}
|
||||||
|
let _ = cmd.output();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for KeyboardStep {
|
impl Default for KeyboardStep {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
|
let (layouts, variants) = load_keyboard_layouts();
|
||||||
Self {
|
Self {
|
||||||
test_input: String::new(),
|
test_input: String::new(),
|
||||||
layouts: vec![
|
layout_filter: String::new(),
|
||||||
("tr", "Türkçe (Q)"),
|
variant_filter: String::new(),
|
||||||
("tr:f", "Türkçe (F)"),
|
layouts,
|
||||||
("us", "İngilizce (US)"),
|
variants,
|
||||||
("de", "Almanca"),
|
|
||||||
("fr", "Fransızca"),
|
|
||||||
("es", "İspanyolca"),
|
|
||||||
("ru", "Rusça"),
|
|
||||||
("ar", "Arapça"),
|
|
||||||
],
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -42,6 +151,7 @@ impl InstallerStep for KeyboardStep {
|
|||||||
"us".to_string()
|
"us".to_string()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
apply_system_keyboard_layout(&state.keyboard_layout, &state.keyboard_variant);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn show(&mut self, ui: &mut egui::Ui, state: &mut GlobalState) {
|
fn show(&mut self, ui: &mut egui::Ui, state: &mut GlobalState) {
|
||||||
@@ -50,51 +160,128 @@ impl InstallerStep for KeyboardStep {
|
|||||||
|
|
||||||
ui.add_space(12.0);
|
ui.add_space(12.0);
|
||||||
|
|
||||||
|
let available_variants: Vec<_> = self
|
||||||
|
.variants
|
||||||
|
.iter()
|
||||||
|
.filter(|(layout, _, _)| layout == &state.keyboard_layout)
|
||||||
|
.collect();
|
||||||
|
|
||||||
ui.columns(2, |cols| {
|
ui.columns(2, |cols| {
|
||||||
// Sol: Düzen listesi
|
cols[0].vertical(|ui| {
|
||||||
cols[0].label(t!("keyboard_layout_label"));
|
egui::Frame::group(ui.style())
|
||||||
egui::ScrollArea::vertical()
|
.fill(theme::c_bg_widget())
|
||||||
.id_source("kbd_layouts")
|
.stroke(egui::Stroke::new(1.0, egui::Color32::from_gray(200)))
|
||||||
.max_height(260.0)
|
.rounding(egui::Rounding::same(8.0))
|
||||||
.show(&mut cols[0], |ui| {
|
.inner_margin(egui::Margin::same(8.0))
|
||||||
// &(code, display) pattern ile code: &str, display: &str olur
|
.show(ui, |ui| {
|
||||||
for &(code, display) in &self.layouts {
|
ui.label(t!("keyboard_layout_label"));
|
||||||
// tr:f kodunu layout+variant olarak ayır
|
ui.add(
|
||||||
let (layout, variant) = if let Some(pos) = code.find(':') {
|
egui::TextEdit::singleline(&mut self.layout_filter)
|
||||||
(&code[..pos], &code[pos + 1..])
|
.hint_text(t!("keyboard_layout_search_hint")),
|
||||||
} else {
|
);
|
||||||
(code, "")
|
ui.add_space(4.0);
|
||||||
};
|
|
||||||
|
|
||||||
let is_selected = state.keyboard_layout == layout
|
egui::ScrollArea::vertical()
|
||||||
&& state.keyboard_variant == variant;
|
.id_source("kbd_layout_scroll")
|
||||||
|
.auto_shrink([false, false])
|
||||||
|
.max_height(240.0)
|
||||||
|
.show(ui, |ui| {
|
||||||
|
let filter = self.layout_filter.to_lowercase();
|
||||||
|
let filtered_layouts: Vec<_> = self.layouts.iter()
|
||||||
|
.filter(|(code, display)| {
|
||||||
|
filter.is_empty()
|
||||||
|
|| code.to_lowercase().contains(&filter)
|
||||||
|
|| display.to_lowercase().contains(&filter)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
if ui.selectable_label(is_selected, display).clicked() {
|
if filtered_layouts.is_empty() {
|
||||||
state.keyboard_layout = layout.to_string();
|
ui.label(t!("keyboard_layout_search_no_match"));
|
||||||
state.keyboard_variant = variant.to_string();
|
}
|
||||||
}
|
for (code, display) in filtered_layouts {
|
||||||
}
|
let is_selected = state.keyboard_layout == *code;
|
||||||
});
|
if ui.selectable_label(is_selected, display).clicked() {
|
||||||
|
state.keyboard_layout = code.to_string();
|
||||||
|
self.variant_filter.clear();
|
||||||
|
if !self.variants.iter().any(|(layout, _, _)| layout == code) {
|
||||||
|
state.keyboard_variant.clear();
|
||||||
|
}
|
||||||
|
apply_system_keyboard_layout(&state.keyboard_layout, &state.keyboard_variant);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// Sağ: Test alanı
|
cols[1].vertical(|ui| {
|
||||||
cols[1].label(t!("keyboard_test_label"));
|
egui::Frame::group(ui.style())
|
||||||
cols[1].add_space(4.0);
|
.fill(theme::c_bg_widget())
|
||||||
cols[1].add(
|
.stroke(egui::Stroke::new(1.0, egui::Color32::from_gray(200)))
|
||||||
egui::TextEdit::multiline(&mut self.test_input)
|
.rounding(egui::Rounding::same(8.0))
|
||||||
.desired_rows(5)
|
.inner_margin(egui::Margin::same(8.0))
|
||||||
.hint_text(t!("keyboard_test_hint")),
|
.show(ui, |ui| {
|
||||||
);
|
ui.label(t!("keyboard_variant_label"));
|
||||||
|
ui.add(
|
||||||
|
egui::TextEdit::singleline(&mut self.variant_filter)
|
||||||
|
.hint_text(t!("keyboard_variant_search_hint")),
|
||||||
|
);
|
||||||
|
ui.add_space(4.0);
|
||||||
|
|
||||||
cols[1].add_space(8.0);
|
egui::ScrollArea::vertical()
|
||||||
cols[1].label(format!("{}: {}", t!("keyboard_selected"), {
|
.id_source("kbd_variant_scroll")
|
||||||
let variant_part = if state.keyboard_variant.is_empty() {
|
.auto_shrink([false, false])
|
||||||
String::new()
|
.max_height(240.0)
|
||||||
} else {
|
.show(ui, |ui| {
|
||||||
format!(" ({})", state.keyboard_variant)
|
let variant_filter = self.variant_filter.to_lowercase();
|
||||||
};
|
let filtered_variants: Vec<_> = available_variants
|
||||||
format!("{}{}", state.keyboard_layout, variant_part)
|
.into_iter()
|
||||||
}));
|
.filter(|(_, variant, display)| {
|
||||||
|
variant_filter.is_empty()
|
||||||
|
|| variant.to_lowercase().contains(&variant_filter)
|
||||||
|
|| display.to_lowercase().contains(&variant_filter)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let mut selected_default = state.keyboard_variant.is_empty();
|
||||||
|
if ui.selectable_value(&mut selected_default, true, t!("keyboard_variant_default")).clicked() {
|
||||||
|
state.keyboard_variant.clear();
|
||||||
|
apply_system_keyboard_layout(&state.keyboard_layout, "");
|
||||||
|
}
|
||||||
|
if filtered_variants.is_empty() {
|
||||||
|
ui.label(t!("keyboard_variant_search_no_match"));
|
||||||
|
}
|
||||||
|
for (_, variant, display) in filtered_variants {
|
||||||
|
let is_selected = state.keyboard_variant == *variant;
|
||||||
|
if ui.selectable_label(is_selected, display).clicked() {
|
||||||
|
state.keyboard_variant = variant.to_string();
|
||||||
|
apply_system_keyboard_layout(&state.keyboard_layout, &state.keyboard_variant);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
ui.add_space(12.0);
|
||||||
|
ui.label(t!("keyboard_test_label"));
|
||||||
|
ui.add_space(4.0);
|
||||||
|
ui.add(
|
||||||
|
egui::TextEdit::multiline(&mut self.test_input)
|
||||||
|
.desired_rows(5)
|
||||||
|
.desired_width(ui.available_width())
|
||||||
|
.hint_text(t!("keyboard_test_hint")),
|
||||||
|
);
|
||||||
|
|
||||||
|
ui.add_space(8.0);
|
||||||
|
ui.label(format!("{}: {}", t!("keyboard_selected"), {
|
||||||
|
let layout_name = layout_display_name(&state.keyboard_layout, &self.layouts);
|
||||||
|
if state.keyboard_variant.is_empty() {
|
||||||
|
layout_name
|
||||||
|
} else {
|
||||||
|
let variant_name = variant_display_name(&state.keyboard_layout, &state.keyboard_variant, &self.variants);
|
||||||
|
format!("{} ({})", layout_name, variant_name)
|
||||||
|
}
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_complete(&self, state: &GlobalState) -> bool {
|
fn is_complete(&self, state: &GlobalState) -> bool {
|
||||||
|
|||||||
+20
-1
@@ -77,6 +77,24 @@ fn is_valid_hostname(s: &str) -> bool {
|
|||||||
s.chars().all(|c| c.is_ascii_alphanumeric() || c == '-')
|
s.chars().all(|c| c.is_ascii_alphanumeric() || c == '-')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Otomatik hostname üretirken nokta, boşluk ve geçersiz karakterleri kaldırır.
|
||||||
|
fn sanitize_hostname(s: &str) -> String {
|
||||||
|
let mut hostname: String = s
|
||||||
|
.chars()
|
||||||
|
.filter(|c| c.is_ascii_alphanumeric())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
if hostname.len() > 63 {
|
||||||
|
hostname.truncate(63);
|
||||||
|
}
|
||||||
|
|
||||||
|
if hostname.is_empty() {
|
||||||
|
"host".to_string()
|
||||||
|
} else {
|
||||||
|
hostname
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn password_strength(p: &str) -> (String, egui::Color32) {
|
fn password_strength(p: &str) -> (String, egui::Color32) {
|
||||||
let len = p.len();
|
let len = p.len();
|
||||||
let has_upper = p.chars().any(|c| c.is_uppercase());
|
let has_upper = p.chars().any(|c| c.is_uppercase());
|
||||||
@@ -112,13 +130,14 @@ impl InstallerStep for UsersStep {
|
|||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
.trim()
|
.trim()
|
||||||
.to_string();
|
.to_string();
|
||||||
|
let sys_hostname = sanitize_hostname(&sys_hostname);
|
||||||
if !sys_hostname.is_empty() && sys_hostname != "localhost" {
|
if !sys_hostname.is_empty() && sys_hostname != "localhost" {
|
||||||
state.hostname = sys_hostname;
|
state.hostname = sys_hostname;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Hâlâ boşsa ve username varsa username-pc öner
|
// Hâlâ boşsa ve username varsa username-pc öner
|
||||||
if state.hostname.is_empty() && !state.username.is_empty() {
|
if state.hostname.is_empty() && !state.username.is_empty() {
|
||||||
state.hostname = format!("{}-pc", state.username);
|
state.hostname = sanitize_hostname(&format!("{}pc", state.username));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -176,7 +176,7 @@ fn build_style() -> egui::Style {
|
|||||||
style.spacing.item_spacing = Vec2::new(8.0, 6.0);
|
style.spacing.item_spacing = Vec2::new(8.0, 6.0);
|
||||||
style.spacing.button_padding = Vec2::new(12.0, 6.0);
|
style.spacing.button_padding = Vec2::new(12.0, 6.0);
|
||||||
style.spacing.indent = 16.0;
|
style.spacing.indent = 16.0;
|
||||||
style.spacing.scroll.bar_width = 8.0;
|
style.spacing.scroll.bar_width = 14.0;
|
||||||
|
|
||||||
// Metin boyutları
|
// Metin boyutları
|
||||||
style.text_styles = [
|
style.text_styles = [
|
||||||
|
|||||||
Reference in New Issue
Block a user