diff --git a/src/steps/users.rs b/src/steps/users.rs index 83550b0..dc6ded2 100644 --- a/src/steps/users.rs +++ b/src/steps/users.rs @@ -155,7 +155,7 @@ impl InstallerStep for UsersStep { // ─── Kullanıcı Bilgileri Formu ────────────────────────────────── egui::Frame::group(ui.style()) - .fill(egui::Color32::from_rgb(240, 243, 198)) + .fill(theme::c_bg_widget()) .rounding(6.0) .inner_margin(egui::Margin::symmetric(10.0, 10.0)) .show(ui, |ui| { diff --git a/src/ui/kle_svg copy.rs b/src/ui/kle_svg copy.rs new file mode 100644 index 0000000..d2d904e --- /dev/null +++ b/src/ui/kle_svg copy.rs @@ -0,0 +1,302 @@ +use eframe::egui::{self, Rect, Rounding, Stroke, Vec2}; +use xkbcommon::xkb; + +#[derive(Debug, Clone)] +pub struct XkbKey { + pub x: f32, pub y: f32, pub w: f32, pub h: f32, + pub labels: Vec, +} + +#[derive(Debug)] +pub struct XkbLayout { + pub keys: Vec, + pub width: f32, pub height: f32, +} + +/// Boş bir klavye düzeni (layout derlenemediğinde gösterilir). +pub fn empty_layout() -> XkbLayout { + XkbLayout { keys: vec![], width: 1.0, height: 1.0 } +} + +/// ISO‑105 tuşlu klavye için XKB adı → (x, y, w, h) birim cinsinden. +/// x,y ondalıklı olabilir (geniş tuşlar için). +fn key_position(name: &str) -> Option<(f32, f32, f32, f32)> { + // Satır y offset'leri (birim) + let r = |row| row as f32; + + // Standart ISO‑105 düzeni. x değerleri kümülatif. + match name { + // ── (function row tamamen kaldırıldı) ── + + + // ── Number row ───────────────── + "TLDE" => Some((0.0, r(1), 1.0, 1.0)), + "AE01" => Some((1.0, r(1), 1.0, 1.0)), + "AE02" => Some((2.0, r(1), 1.0, 1.0)), + "AE03" => Some((3.0, r(1), 1.0, 1.0)), + "AE04" => Some((4.0, r(1), 1.0, 1.0)), + "AE05" => Some((5.0, r(1), 1.0, 1.0)), + "AE06" => Some((6.0, r(1), 1.0, 1.0)), + "AE07" => Some((7.0, r(1), 1.0, 1.0)), + "AE08" => Some((8.0, r(1), 1.0, 1.0)), + "AE09" => Some((9.0, r(1), 1.0, 1.0)), + "AE10" => Some((10.0, r(1), 1.0, 1.0)), + "AE11" => Some((11.0, r(1), 1.0, 1.0)), + "AE12" => Some((12.0, r(1), 1.0, 1.0)), + "BKSP" => Some((13.0, r(1), 2.0, 1.0)), + + // ── Top alpha row (Q–P) ──────── + "TAB" => Some((0.0, r(2), 1.5, 1.0)), + "AD01" => Some((1.5, r(2), 1.0, 1.0)), + "AD02" => Some((2.5, r(2), 1.0, 1.0)), + "AD03" => Some((3.5, r(2), 1.0, 1.0)), + "AD04" => Some((4.5, r(2), 1.0, 1.0)), + "AD05" => Some((5.5, r(2), 1.0, 1.0)), + "AD06" => Some((6.5, r(2), 1.0, 1.0)), + "AD07" => Some((7.5, r(2), 1.0, 1.0)), + "AD08" => Some((8.5, r(2), 1.0, 1.0)), + "AD09" => Some((9.5, r(2), 1.0, 1.0)), + "AD10" => Some((10.5, r(2), 1.0, 1.0)), + "AD11" => Some((11.5, r(2), 1.0, 1.0)), + "AD12" => Some((12.5, r(2), 1.0, 1.0)), + "BKSL" => Some((13.5, r(2), 1.0, 1.0)), // ISO: solundaki tuş (\|) + "RTRN" => Some((14.5, r(2), 1.25, 2.0)), // ISO Enter (2 satır) + + // ── Home row (A–L) ──────────── + "CAPS" => Some((0.0, r(3), 1.75, 1.0)), + "AC01" => Some((1.75, r(3), 1.0, 1.0)), + "AC02" => Some((2.75, r(3), 1.0, 1.0)), + "AC03" => Some((3.75, r(3), 1.0, 1.0)), + "AC04" => Some((4.75, r(3), 1.0, 1.0)), + "AC05" => Some((5.75, r(3), 1.0, 1.0)), + "AC06" => Some((6.75, r(3), 1.0, 1.0)), + "AC07" => Some((7.75, r(3), 1.0, 1.0)), + "AC08" => Some((8.75, r(3), 1.0, 1.0)), + "AC09" => Some((9.75, r(3), 1.0, 1.0)), + "AC10" => Some((10.75,r(3), 1.0, 1.0)), + "AC11" => Some((11.75,r(3), 1.0, 1.0)), + "AC12" => Some((12.75,r(3), 1.0, 1.0)), + + // ── Bottom row (Z–M) ────────── + "LFSH" => Some((0.0, r(4), 1.25, 1.0)), + "LSGT" => Some((1.25, r(4), 1.0, 1.0)), // ISO extra (< > |) + "AB01" => Some((2.25, r(4), 1.0, 1.0)), + "AB02" => Some((3.25, r(4), 1.0, 1.0)), + "AB03" => Some((4.25, r(4), 1.0, 1.0)), + "AB04" => Some((5.25, r(4), 1.0, 1.0)), + "AB05" => Some((6.25, r(4), 1.0, 1.0)), + "AB06" => Some((7.25, r(4), 1.0, 1.0)), + "AB07" => Some((8.25, r(4), 1.0, 1.0)), + "AB08" => Some((9.25, r(4), 1.0, 1.0)), + "AB09" => Some((10.25,r(4), 1.0, 1.0)), + "AB10" => Some((11.25,r(4), 1.0, 1.0)), + "RTSH" => Some((12.25,r(4), 3.75, 1.0)), + + // ── Space bar row ───────────── + "LCTL" => Some((0.0, r(5), 1.25, 1.0)), + "LWIN" => Some((1.25, r(5), 1.25, 1.0)), + "LALT" => Some((2.5, r(5), 1.5, 1.0)), + "SPCE" => Some((4.0, r(5), 6.25, 1.0)), + "RALT" | "LVL3" => Some((10.25,r(5), 1.5, 1.0)), + "COMP" => Some((11.75,r(5), 1.25, 1.0)), + "RCTL" => Some((13.0, r(5), 1.25, 1.0)), + + // ── (nav cluster tamamen kaldırıldı) ── + + _ => None, + } +} + +fn keysym_to_label(sym: xkb::Keysym) -> String { + let utf = xkb::keysym_to_utf8(sym); + if utf.chars().any(|c| !c.is_control()) { + let trimmed = utf.trim(); + if !trimmed.is_empty() { + return trimmed.to_string(); + } + } + + let name = xkb::keysym_get_name(sym); + if name == "VoidSymbol" || name == "NoSymbol" { + return String::new(); + } + + // Yaygın XKB sembol adlarını kısa etiketlere çevir + match name.as_str() { + "BackSpace" => "Bksp".into(), + "Tab" => "Tab".into(), + "Return" => "Enter".into(), + "Caps_Lock" => "Caps".into(), + "Shift_L" => "Shift".into(), + "Shift_R" => "Shift".into(), + "Control_L" => "Ctrl".into(), + "Control_R" => "Ctrl".into(), + "Alt_L" => "Alt".into(), + "Alt_R" => "AltGr".into(), + "ISO_Level3_Shift" => "AltGr".into(), + "Super_L" => "Win".into(), + "Super_R" => "Win".into(), + "Menu" => "Menü".into(), + "Escape" => "Esc".into(), + "Delete" => "Del".into(), + "Insert" => "Ins".into(), + "Prior" => "PgUp".into(), + "Next" => "PgDn".into(), + "space" => "".into(), + "Multi_key" => "Compose".into(), + _ if name.starts_with("XF86") => String::new(), + _ => name, + } +} + +/// XKB keymap'inden klavye düzenini oluşturur. +pub fn compile_layout(layout: &str, variant: &str) -> Option { + let mut context = xkb::Context::new(xkb::CONTEXT_NO_FLAGS); + context.set_log_level(xkb::LogLevel::Critical); + let model = if std::path::Path::new("/usr/share/X11/xkb/symbols/").exists() { + "pc105" + } else { + "pc105" + }; + + let keymap = xkb::Keymap::new_from_names::( + &context, + "", // rules + model, // model + layout, // layout + if variant.is_empty() { "" } else { variant }, // variant + None::, // options + xkb::COMPILE_NO_FLAGS, + )?; + + let min = keymap.min_keycode(); + let max = keymap.max_keycode(); + let mut keys = Vec::new(); + let mut min_x = f32::MAX; + let mut min_y = f32::MAX; + let mut max_x = f32::MIN; + let mut max_y = f32::MIN; + let mut moved_comma = false; + + for code_u32 in (min.raw() as u32)..=(max.raw() as u32) { + let code = xkb::Keycode::new(code_u32); + let Some(name) = keymap.key_get_name(code) else { continue }; + let Some((x, y, w, h)) = key_position(name) else { continue }; + + // Sembolleri al (base, shift, altgr, shift+altgr), boş olanları atla + let mut raw_labels = Vec::new(); + for level in 0..4 { + let syms = keymap.key_get_syms_by_level(code, 0, level); + let label = syms.iter() + .map(|&s| keysym_to_label(s)) + .collect::>() + .join(""); + if !label.is_empty() && !raw_labels.contains(&label) { + raw_labels.push(label); + } + } + + // Her tuşta sadece 1 karakter göster (büyük harf tercihli) + let mut labels: Vec = { + let single: Vec<_> = raw_labels.iter().filter(|l| l.chars().count() == 1).collect(); + if single.is_empty() { + // Hiç tek karakter yoksa ilk label'ı göster (F1, Esc, …) + raw_labels.into_iter().take(1).collect() + } else { + // Büyük harf varsa onu göster, yoksa ilk tek karakteri + let uc = single.iter().find(|l| l.chars().next().map_or(false, |c| c.is_uppercase())); + vec![if let Some(s) = uc { (*s).clone() } else { (*single[0]).clone() }] + } + }; + + // , tuşu BKSL'deyse AC12'ye taşı (Türkçe Q düzeni için) + if name == "BKSL" && labels.contains(&",".to_string()) { + moved_comma = true; + continue; + } + if name == "AC12" { + labels = vec![",".to_string()]; + } + + keys.push(XkbKey { x, y, w, h, labels }); + + if x < min_x { min_x = x; } + if y < min_y { min_y = y; } + if x + w > max_x { max_x = x + w; } + if y + h > max_y { max_y = y + h; } + } + + // AC12 keymap'te yoksa manuel ekle + if moved_comma && !keys.iter().any(|k| (k.x - 12.75).abs() < 0.1 && (k.y - 3.0).abs() < 0.1) { + keys.push(XkbKey { x: 12.75, y: 3.0, w: 1.0, h: 1.0, labels: vec![",".to_string()] }); + } + + // normalize x,y — tüm tuşlar (0,0) bazlı olsun + for key in &mut keys { + key.x -= min_x; + key.y -= min_y; + } + + Some(XkbLayout { + keys, + width: max_x - min_x, + height: max_y - min_y, + }) +} + +/// Klavyeyi egui ile çizer. +pub fn draw_layout(ui: &mut egui::Ui, layout: &XkbLayout, max_size: Vec2) { + let margin = 6.0; + let spacing = 0.08; + let corner = 3.0; + + let uw = (max_size.x - margin * 2.0) / layout.width; + let uh = (max_size.y - margin * 2.0) / layout.height; + let unit = uw.min(uh).max(12.0); + + let draw_w = layout.width * unit; + let draw_h = layout.height * unit; + + let (_id, resp) = ui.allocate_exact_size(Vec2::new(draw_w + margin * 2.0, draw_h + margin * 2.0), egui::Sense::hover()); + let painter = ui.painter_at(resp.rect); + let origin = resp.rect.min; + + let visuals = ui.visuals(); + let key_fill = visuals.widgets.inactive.bg_fill; + let key_stroke = visuals.widgets.noninteractive.bg_stroke; + let text_color = egui::Color32::BLACK; + let bg = visuals.window_fill(); + + painter.rect_filled(resp.rect, Rounding::same(6.0), bg); + + let font_size = (unit * 0.28).max(6.0); + let small_size = (unit * 0.22).max(5.0); + + let sp = unit * spacing; + + for key in &layout.keys { + let kx = origin.x + margin + key.x * unit + sp; + let ky = origin.y + margin + key.y * unit + sp; + let kw = key.w * unit - sp * 2.0; + let kh = key.h * unit - sp * 2.0; + + if kw <= 0.0 || kh <= 0.0 { continue; } + + let key_rect = Rect::from_min_size(egui::pos2(kx, ky), Vec2::new(kw, kh)); + painter.rect(key_rect, Rounding::same(corner), key_fill, Stroke::new(key_stroke.width.max(0.5), key_stroke.color)); + + for (i, label) in key.labels.iter().enumerate() { + let lbl = label.trim(); + if lbl.is_empty() { continue; } + let (lx, ly, sz) = match i { + 0 => (kx + kw * 0.5, ky + kh * 0.5, font_size), + 1 => (kx + kw * 0.5, ky + kh * 0.75, small_size), + _ => (kx + kw * 0.5, ky + kh * 0.9, small_size), + }; + painter.text( + egui::pos2(lx, ly), egui::Align2::CENTER_CENTER, + lbl, egui::FontId::proportional(sz), text_color, + ); + } + } +} diff --git a/src/ui/theme.rs b/src/ui/theme.rs index a99ff35..6d6a13d 100644 --- a/src/ui/theme.rs +++ b/src/ui/theme.rs @@ -28,6 +28,7 @@ static SIDEBAR: AtomicU32 = AtomicU32::new(0x0B0B0D); static ACCENT_COLOR: AtomicU32 = AtomicU32::new(0xd9005b); // Pisi Premium Vurgu (#d9005b) static SIDEBAR_TEXT: AtomicU32 = AtomicU32::new(0x000000); static KEYBOARD_BG: AtomicU32 = AtomicU32::new(0xFFFFFF); +static BG_ROOT: AtomicU32 = AtomicU32::new(0x1A1A1E); @@ -49,7 +50,7 @@ pub fn c_error() -> Color32 { color_from_hex(ERROR.load(Ordering::Relaxed)) pub fn c_sidebar() -> Color32 { color_from_hex(SIDEBAR.load(Ordering::Relaxed)) } pub fn c_sidebar_text() -> Color32 { color_from_hex(SIDEBAR_TEXT.load(Ordering::Relaxed)) } pub fn c_footer_bg() -> Color32 { color_from_hex(BG_FOOTER.load(Ordering::Relaxed)) } -pub fn c_root_bg() -> Color32 { color_from_hex(0xf7f7f7) } +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 set_theme_mode(is_dark: bool) { @@ -63,6 +64,7 @@ pub fn set_theme_mode(is_dark: bool) { TEXT_DIM.store(0x9E9EAF, Ordering::Relaxed); SIDEBAR.store(0x0B0B0D, Ordering::Relaxed); KEYBOARD_BG.store(0x000000, Ordering::Relaxed); + BG_ROOT.store(0x1A1A1E, Ordering::Relaxed); } else { BG_FOOTER.store(0xf7f7f7, Ordering::Relaxed); BG_DARK.store(0xf7f7f7, Ordering::Relaxed); @@ -71,14 +73,15 @@ pub fn set_theme_mode(is_dark: bool) { TEXT_DIM.store(0x000000, Ordering::Relaxed); SIDEBAR_TEXT.store(0xffffff, Ordering::Relaxed); KEYBOARD_BG.store(0xffffff, Ordering::Relaxed); + BG_ROOT.store(0xf7f7f7, Ordering::Relaxed); - // BG_DARK.store(0xF0F0F5, Ordering::Relaxed); - // BG_PANEL.store(0xFFFFFF, Ordering::Relaxed); - // BG_WIDGET.store(0xE8E8EE, Ordering::Relaxed); - // BORDER.store(0xD0D0DA, Ordering::Relaxed); - // TEXT.store(0x20202A, Ordering::Relaxed); - // TEXT_DIM.store(0x60606A, Ordering::Relaxed); - // SIDEBAR.store(0xE4E4EA, Ordering::Relaxed); + BG_DARK.store(0xF0F0F5, Ordering::Relaxed); + BG_PANEL.store(0xFFFFFF, Ordering::Relaxed); + BG_WIDGET.store(0xE8E8EE, Ordering::Relaxed); + BORDER.store(0xD0D0DA, Ordering::Relaxed); + TEXT.store(0x20202A, Ordering::Relaxed); + TEXT_DIM.store(0x60606A, Ordering::Relaxed); + SIDEBAR.store(0xE4E4EA, Ordering::Relaxed); } } @@ -189,13 +192,13 @@ fn build_visuals(is_dark: bool) -> Visuals { v.widgets.noninteractive.bg_fill = c_bg_widget(); // Pasif Hali: Widget arka planı (TextEdit, ComboBox giriş alanı) - v.widgets.inactive.bg_fill = Color32::from_rgb(255, 255, 255); + v.widgets.inactive.bg_fill = if is_dark { Color32::from_rgb(255, 255, 255) } else { Color32::from_rgb(245, 245, 245) }; // Üzerine Gelme Hali: Hafif açık - v.widgets.hovered.bg_fill = Color32::from_rgb(55, 58, 68); + v.widgets.hovered.bg_fill = if is_dark { Color32::from_rgb(55, 58, 68) } else { Color32::from_rgb(235, 235, 235) }; // Tıklanma/Açık Kalma Hali: Daha açık - v.widgets.active.bg_fill = Color32::WHITE; + v.widgets.active.bg_fill = if is_dark { Color32::WHITE } else { Color32::from_rgb(230, 230, 230) }; // ComboBox stilleri (combobox_stil.rs) crate::ui::combobox_stil::apply_visuals(&mut v);