mirror of
https://gitlab.com/erkanisik/yali-rs.git
synced 2026-07-31 03:09:00 +00:00
feat: replace KLE JSON keyboard rendering with xkbcommon
xkbcommon ile XKB keymap derlenip tuş pozisyonları/sembolleri çıkarılıyor, egui Painter ile temaya uygun çiziliyor. KLE JSON indirme/kullanma kalktı, ureq ve serde_json bağımlılıkları kaldırıldı. - compile_layout() — xkb::Keymap::new_from_names() ile keymap derleme - key_position() — ISO‑105 tuşları için ondalıklı x,y pozisyonları - keysym_to_label() — xkbcommon keysym → kısa etiket (⌫, ⇥, AltGr, …) - draw_layout() — egui Painter ile ölçeklenebilir klavye çizimi - Her tuşta tek karakter gösterimi (büyük harf tercihli) - keyboard_layout boşken ilk show()'da varsayılana set edilir - Eski KLE JSON önbellek dosyaları silindi
This commit is contained in:
@@ -16,6 +16,7 @@ rust-i18n = "3.1"
|
|||||||
lazy_static = "1.4"
|
lazy_static = "1.4"
|
||||||
async-trait = "0.1"
|
async-trait = "0.1"
|
||||||
chrono = "0.4"
|
chrono = "0.4"
|
||||||
|
xkbcommon = "0.9"
|
||||||
|
|
||||||
[patch.crates-io]
|
[patch.crates-io]
|
||||||
# Yerel mudur veya comar-api kütüphaneleri buraya eklenebilir
|
# Yerel mudur veya comar-api kütüphaneleri buraya eklenebilir
|
||||||
|
|||||||
+23
-22
@@ -211,6 +211,7 @@ struct YaliApp {
|
|||||||
last_step: usize,
|
last_step: usize,
|
||||||
branding: branding::BrandingConfig,
|
branding: branding::BrandingConfig,
|
||||||
loaded_language: String,
|
loaded_language: String,
|
||||||
|
#[allow(dead_code)]
|
||||||
show_cancel_dialog: bool,
|
show_cancel_dialog: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -451,9 +452,9 @@ impl eframe::App for YaliApp {
|
|||||||
} else {
|
} else {
|
||||||
t!("next")
|
t!("next")
|
||||||
};
|
};
|
||||||
if !is_exec && !is_finish && ui::theme::danger_button(ui, &t!("cancel")).clicked() {
|
// if !is_exec && !is_finish && ui::theme::danger_button(ui, &t!("cancel")).clicked() {
|
||||||
self.show_cancel_dialog = true;
|
// self.show_cancel_dialog = true;
|
||||||
};
|
// };
|
||||||
let next_clicked = if is_valid {
|
let next_clicked = if is_valid {
|
||||||
ui::theme::primary_button(ui, &next_label).clicked()
|
ui::theme::primary_button(ui, &next_label).clicked()
|
||||||
} else {
|
} else {
|
||||||
@@ -476,25 +477,25 @@ impl eframe::App for YaliApp {
|
|||||||
ui.add_space(10.0);
|
ui.add_space(10.0);
|
||||||
});
|
});
|
||||||
|
|
||||||
// ── İptal onay diyaloğu ───────────────────────────────
|
// // ── İptal onay diyaloğu ───────────────────────────────
|
||||||
if self.show_cancel_dialog {
|
// if self.show_cancel_dialog {
|
||||||
egui::Window::new(t!("cancel_confirm_title"))
|
// egui::Window::new(t!("cancel_confirm_title"))
|
||||||
.collapsible(false)
|
// .collapsible(false)
|
||||||
.resizable(false)
|
// .resizable(false)
|
||||||
.anchor(egui::Align2::CENTER_CENTER, [0.0, 0.0])
|
// .anchor(egui::Align2::CENTER_CENTER, [0.0, 0.0])
|
||||||
.show(ctx, |ui| {
|
// .show(ctx, |ui| {
|
||||||
ui.label(t!("cancel_confirm_msg"));
|
// ui.label(t!("cancel_confirm_msg"));
|
||||||
ui.add_space(12.0);
|
// ui.add_space(12.0);
|
||||||
ui.horizontal(|ui| {
|
// ui.horizontal(|ui| {
|
||||||
if ui.button(t!("cancel_confirm_yes")).clicked() {
|
// if ui.button(t!("cancel_confirm_yes")).clicked() {
|
||||||
ctx.send_viewport_cmd(egui::ViewportCommand::Close);
|
// ctx.send_viewport_cmd(egui::ViewportCommand::Close);
|
||||||
}
|
// }
|
||||||
if ui.button(t!("cancel_confirm_no")).clicked() {
|
// if ui.button(t!("cancel_confirm_no")).clicked() {
|
||||||
self.show_cancel_dialog = false;
|
// self.show_cancel_dialog = false;
|
||||||
}
|
// }
|
||||||
});
|
// });
|
||||||
});
|
// });
|
||||||
}
|
// }
|
||||||
|
|
||||||
// ── Merkez panel ─────────────────────────────────────
|
// ── Merkez panel ─────────────────────────────────────
|
||||||
egui::CentralPanel::default()
|
egui::CentralPanel::default()
|
||||||
|
|||||||
+67
-100
@@ -1,4 +1,4 @@
|
|||||||
use eframe::egui;
|
use eframe::egui::{self, Color32};
|
||||||
use rust_i18n::t;
|
use rust_i18n::t;
|
||||||
use crate::installer::{GlobalState, InstallerStep};
|
use crate::installer::{GlobalState, InstallerStep};
|
||||||
use crate::ui::theme;
|
use crate::ui::theme;
|
||||||
@@ -8,8 +8,6 @@ use crate::ui::theme;
|
|||||||
/// Dosya okunamazsa sabit bir temel liste kullanılır.
|
/// Dosya okunamazsa sabit bir temel liste kullanılır.
|
||||||
pub struct KeyboardStep {
|
pub struct KeyboardStep {
|
||||||
test_input: String,
|
test_input: String,
|
||||||
layout_filter: String,
|
|
||||||
variant_filter: String,
|
|
||||||
layouts: Vec<(String, String)>, // (kod, gösterim adı)
|
layouts: Vec<(String, String)>, // (kod, gösterim adı)
|
||||||
variants: Vec<(String, String, String)>, // (layout, variant, gösterim adı)
|
variants: Vec<(String, String, String)>, // (layout, variant, gösterim adı)
|
||||||
}
|
}
|
||||||
@@ -17,6 +15,7 @@ pub struct KeyboardStep {
|
|||||||
fn default_keyboard_layouts() -> Vec<(String, String)> {
|
fn default_keyboard_layouts() -> Vec<(String, String)> {
|
||||||
vec![
|
vec![
|
||||||
("tr".to_string(), "Türkçe (Q)".to_string()),
|
("tr".to_string(), "Türkçe (Q)".to_string()),
|
||||||
|
("az".to_string(), "Azərbaycanca".to_string()),
|
||||||
("us".to_string(), "İngilizce (US)".to_string()),
|
("us".to_string(), "İngilizce (US)".to_string()),
|
||||||
("de".to_string(), "Almanca".to_string()),
|
("de".to_string(), "Almanca".to_string()),
|
||||||
("fr".to_string(), "Fransızca".to_string()),
|
("fr".to_string(), "Fransızca".to_string()),
|
||||||
@@ -29,6 +28,7 @@ fn default_keyboard_layouts() -> Vec<(String, String)> {
|
|||||||
fn default_keyboard_variants() -> Vec<(String, String, String)> {
|
fn default_keyboard_variants() -> Vec<(String, String, String)> {
|
||||||
vec![
|
vec![
|
||||||
("tr".to_string(), "f".to_string(), "Türkçe (F)".to_string()),
|
("tr".to_string(), "f".to_string(), "Türkçe (F)".to_string()),
|
||||||
|
("az".to_string(), "cyrillic".to_string(), "Azərbaycan (Kiril)".to_string()),
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,8 +129,6 @@ impl Default for KeyboardStep {
|
|||||||
let (layouts, variants) = load_keyboard_layouts();
|
let (layouts, variants) = load_keyboard_layouts();
|
||||||
Self {
|
Self {
|
||||||
test_input: String::new(),
|
test_input: String::new(),
|
||||||
layout_filter: String::new(),
|
|
||||||
variant_filter: String::new(),
|
|
||||||
layouts,
|
layouts,
|
||||||
variants,
|
variants,
|
||||||
}
|
}
|
||||||
@@ -155,122 +153,91 @@ impl InstallerStep for KeyboardStep {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn show(&mut self, ui: &mut egui::Ui, state: &mut GlobalState) {
|
fn show(&mut self, ui: &mut egui::Ui, state: &mut GlobalState) {
|
||||||
|
if state.keyboard_layout.is_empty() {
|
||||||
|
state.keyboard_layout = if state.language == "tr" { "tr" } else { "us" }.to_string();
|
||||||
|
}
|
||||||
|
|
||||||
ui.heading(t!("keyboard_title"));
|
ui.heading(t!("keyboard_title"));
|
||||||
ui.label(t!("keyboard_description"));
|
ui.label(t!("keyboard_description"));
|
||||||
|
|
||||||
ui.add_space(12.0);
|
ui.add_space(12.0);
|
||||||
|
|
||||||
|
// ── Klavye görseli (xkbcommon ile çizilir, temaya uyumlu) ─
|
||||||
|
if let Some(kle) = crate::ui::kle_svg::compile_layout(&state.keyboard_layout, &state.keyboard_variant) {
|
||||||
|
let max = egui::Vec2::new(ui.available_width(), ui.available_height().max(100.0));
|
||||||
|
crate::ui::kle_svg::draw_layout(ui, &kle, max);
|
||||||
|
}
|
||||||
|
|
||||||
|
ui.add_space(12.0);
|
||||||
|
|
||||||
|
// ── Layout ve Varyant ComboBox'ları yan yana ──────────────
|
||||||
let available_variants: Vec<_> = self
|
let available_variants: Vec<_> = self
|
||||||
.variants
|
.variants
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|(layout, _, _)| layout == &state.keyboard_layout)
|
.filter(|(layout, _, _)| layout == &state.keyboard_layout)
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
ui.columns(2, |cols| {
|
let current_variant_name = if state.keyboard_variant.is_empty() {
|
||||||
cols[0].vertical(|ui| {
|
t!("keyboard_variant_default").to_string()
|
||||||
egui::Frame::group(ui.style())
|
} else {
|
||||||
.fill(theme::c_bg_widget())
|
let vn = variant_display_name(&state.keyboard_layout, &state.keyboard_variant, &self.variants);
|
||||||
.stroke(egui::Stroke::new(1.0, egui::Color32::from_gray(200)))
|
if vn.is_empty() { state.keyboard_variant.clone() } else { vn }
|
||||||
.rounding(egui::Rounding::same(8.0))
|
};
|
||||||
.inner_margin(egui::Margin::same(8.0))
|
let current_layout_name = layout_display_name(&state.keyboard_layout, &self.layouts);
|
||||||
.show(ui, |ui| {
|
|
||||||
ui.label(t!("keyboard_layout_label"));
|
|
||||||
ui.add(
|
|
||||||
egui::TextEdit::singleline(&mut self.layout_filter)
|
|
||||||
.hint_text(t!("keyboard_layout_search_hint")),
|
|
||||||
);
|
|
||||||
ui.add_space(4.0);
|
|
||||||
|
|
||||||
egui::ScrollArea::vertical()
|
ui.horizontal(|ui| {
|
||||||
.id_source("kbd_layout_scroll")
|
ui.vertical(|ui| {
|
||||||
.auto_shrink([false, false])
|
ui.add_space(6.0);
|
||||||
.max_height(240.0)
|
ui.label(egui::RichText::new(t!("keyboard_layout_label")).strong().color(theme::c_text()));
|
||||||
.show(ui, |ui| {
|
});
|
||||||
let filter = self.layout_filter.to_lowercase();
|
ui.add_space(8.0);
|
||||||
let filtered_layouts: Vec<_> = self.layouts.iter()
|
let lw = ui.available_width().min(200.0);
|
||||||
.filter(|(code, display)| {
|
theme::show_combo_box(ui, "kbd_layout", current_layout_name.as_str(), lw, |ui, w| {
|
||||||
filter.is_empty()
|
for (code, display) in &self.layouts {
|
||||||
|| code.to_lowercase().contains(&filter)
|
if theme::selectable_value(ui, &mut state.keyboard_layout, code.clone(), display.as_str(), w).clicked() {
|
||||||
|| display.to_lowercase().contains(&filter)
|
if !self.variants.iter().any(|(layout, _, _)| layout == code) {
|
||||||
})
|
state.keyboard_variant.clear();
|
||||||
.collect();
|
}
|
||||||
|
apply_system_keyboard_layout(&state.keyboard_layout, &state.keyboard_variant);
|
||||||
if filtered_layouts.is_empty() {
|
}
|
||||||
ui.label(t!("keyboard_layout_search_no_match"));
|
}
|
||||||
}
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
cols[1].vertical(|ui| {
|
ui.add_space(16.0);
|
||||||
egui::Frame::group(ui.style())
|
|
||||||
.fill(theme::c_bg_widget())
|
|
||||||
.stroke(egui::Stroke::new(1.0, egui::Color32::from_gray(200)))
|
|
||||||
.rounding(egui::Rounding::same(8.0))
|
|
||||||
.inner_margin(egui::Margin::same(8.0))
|
|
||||||
.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);
|
|
||||||
|
|
||||||
egui::ScrollArea::vertical()
|
ui.vertical(|ui| {
|
||||||
.id_source("kbd_variant_scroll")
|
ui.add_space(6.0);
|
||||||
.auto_shrink([false, false])
|
ui.label(egui::RichText::new(t!("keyboard_variant_label")).strong().color(theme::c_text()));
|
||||||
.max_height(240.0)
|
});
|
||||||
.show(ui, |ui| {
|
ui.add_space(8.0);
|
||||||
let variant_filter = self.variant_filter.to_lowercase();
|
let vw = ui.available_width().min(200.0);
|
||||||
let filtered_variants: Vec<_> = available_variants
|
theme::show_combo_box(ui, "kbd_variant", current_variant_name.as_str(), vw, |ui, w| {
|
||||||
.into_iter()
|
let default_variant = String::new();
|
||||||
.filter(|(_, variant, display)| {
|
if theme::selectable_value(ui, &mut state.keyboard_variant, default_variant, &t!("keyboard_variant_default"), w).clicked() {
|
||||||
variant_filter.is_empty()
|
apply_system_keyboard_layout(&state.keyboard_layout, "");
|
||||||
|| variant.to_lowercase().contains(&variant_filter)
|
}
|
||||||
|| display.to_lowercase().contains(&variant_filter)
|
for (_, variant, display) in &available_variants {
|
||||||
})
|
if theme::selectable_value(ui, &mut state.keyboard_variant, variant.clone(), display.as_str(), w).clicked() {
|
||||||
.collect();
|
apply_system_keyboard_layout(&state.keyboard_layout, &state.keyboard_variant);
|
||||||
|
}
|
||||||
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.add_space(12.0);
|
||||||
ui.label(t!("keyboard_test_label"));
|
ui.label(t!("keyboard_test_label"));
|
||||||
ui.add_space(4.0);
|
ui.add_space(4.0);
|
||||||
ui.add(
|
egui::Frame::group(ui.style())
|
||||||
egui::TextEdit::multiline(&mut self.test_input)
|
.fill(Color32::from_rgb(255, 255, 255))
|
||||||
.desired_rows(5)
|
.rounding(6.0)
|
||||||
.desired_width(ui.available_width())
|
.show(ui, |ui| {
|
||||||
.hint_text(t!("keyboard_test_hint")),
|
ui.add(
|
||||||
);
|
egui::TextEdit::singleline(&mut self.test_input)
|
||||||
|
.desired_width(ui.available_width())
|
||||||
|
.hint_text(t!("keyboard_test_hint"))
|
||||||
|
.frame(false),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
ui.add_space(8.0);
|
ui.add_space(8.0);
|
||||||
ui.label(format!("{}: {}", t!("keyboard_selected"), {
|
ui.label(format!("{}: {}", t!("keyboard_selected"), {
|
||||||
|
|||||||
+7
-6
@@ -87,12 +87,13 @@ pub fn primary_button(ui: &mut egui::Ui, text: &str) -> egui::Response {
|
|||||||
|
|
||||||
pub fn secondary_button(ui: &mut egui::Ui, text: &str) -> egui::Response {
|
pub fn secondary_button(ui: &mut egui::Ui, text: &str) -> egui::Response {
|
||||||
gradient_button(ui, text,
|
gradient_button(ui, text,
|
||||||
Color32::from_rgb(130, 140, 150),
|
Color32::from_rgb(130, 140, 150), // top
|
||||||
Color32::from_rgb(100, 110, 120),
|
Color32::from_rgb(100, 110, 120), // bottom
|
||||||
Color32::from_rgb(158, 158, 175),
|
Color32::from_rgb(255, 255, 255), // text color32
|
||||||
Color32::from_rgb(150, 160, 170),
|
Color32::from_rgb(150, 160, 170), // hover top
|
||||||
Color32::from_rgb(120, 130, 140),
|
Color32::from_rgb(120, 130, 140), // hover bottom
|
||||||
Color32::from_rgb(100, 110, 120))
|
Color32::from_rgb(100, 110, 120) // border
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn danger_button(ui: &mut egui::Ui, text: &str) -> egui::Response {
|
pub fn danger_button(ui: &mut egui::Ui, text: &str) -> egui::Response {
|
||||||
|
|||||||
@@ -0,0 +1,334 @@
|
|||||||
|
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<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct XkbLayout {
|
||||||
|
pub keys: Vec<XkbKey>,
|
||||||
|
pub width: f32, pub height: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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 ────────────────
|
||||||
|
"ESC" => Some((0.0, r(0), 1.0, 1.0)),
|
||||||
|
"FK01" => Some((2.0, r(0), 1.0, 1.0)),
|
||||||
|
"FK02" => Some((3.0, r(0), 1.0, 1.0)),
|
||||||
|
"FK03" => Some((4.0, r(0), 1.0, 1.0)),
|
||||||
|
"FK04" => Some((5.0, r(0), 1.0, 1.0)),
|
||||||
|
"FK05" => Some((6.0, r(0), 1.0, 1.0)),
|
||||||
|
"FK06" => Some((7.0, r(0), 1.0, 1.0)),
|
||||||
|
"FK07" => Some((8.0, r(0), 1.0, 1.0)),
|
||||||
|
"FK08" => Some((9.0, r(0), 1.0, 1.0)),
|
||||||
|
"FK09" => Some((10.0, r(0), 1.0, 1.0)),
|
||||||
|
"FK10" => Some((11.0, r(0), 1.0, 1.0)),
|
||||||
|
"FK11" => Some((12.0, r(0), 1.0, 1.0)),
|
||||||
|
"FK12" => Some((13.0, r(0), 1.0, 1.0)),
|
||||||
|
"PRSC" => Some((15.0, r(0), 1.0, 1.0)),
|
||||||
|
"SCLK" => Some((16.0, r(0), 1.0, 1.0)),
|
||||||
|
"PAUS" => Some((17.0, r(0), 1.0, 1.0)),
|
||||||
|
|
||||||
|
// ── 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((14.0, r(2), 1.5, 1.0)), // ISO: solundaki tuş (\|)
|
||||||
|
"RTRN" => Some((14.0, r(2), 1.5, 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)),
|
||||||
|
"AB11" => Some((12.25,r(4), 1.0, 1.0)),
|
||||||
|
"RTSH" => Some((13.25,r(4), 2.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)),
|
||||||
|
"RWIN" => Some((11.75,r(5), 1.25, 1.0)),
|
||||||
|
"COMP" => Some((13.0, r(5), 1.25, 1.0)),
|
||||||
|
"RCTL" => Some((14.25,r(5), 1.25, 1.0)),
|
||||||
|
|
||||||
|
// ── Nav cluster ───────────────
|
||||||
|
"INS" => Some((15.5, r(2), 1.0, 1.0)),
|
||||||
|
"HOME" => Some((16.5, r(2), 1.0, 1.0)),
|
||||||
|
"PGUP" => Some((17.5, r(2), 1.0, 1.0)),
|
||||||
|
"DELE" => Some((15.5, r(3), 1.0, 1.0)),
|
||||||
|
"END" => Some((16.5, r(3), 1.0, 1.0)),
|
||||||
|
"PGDN" => Some((17.5, r(3), 1.0, 1.0)),
|
||||||
|
"UP" => Some((16.5, r(4), 1.0, 1.0)),
|
||||||
|
"LEFT" => Some((15.5, r(5), 1.0, 1.0)),
|
||||||
|
"DOWN" => Some((16.5, r(5), 1.0, 1.0)),
|
||||||
|
"RGHT" => Some((17.5, r(5), 1.0, 1.0)),
|
||||||
|
|
||||||
|
// ── Numpad ────────────────────
|
||||||
|
"NMLK" => Some((19.0, r(2), 1.0, 1.0)),
|
||||||
|
"KSLU" => Some((20.0, r(2), 1.0, 1.0)), // /
|
||||||
|
"KAST" => Some((21.0, r(2), 1.0, 1.0)), // *
|
||||||
|
"KMIN" => Some((22.0, r(2), 1.0, 1.0)), // -
|
||||||
|
"KP7" => Some((19.0, r(3), 1.0, 1.0)),
|
||||||
|
"KP8" => Some((20.0, r(3), 1.0, 1.0)),
|
||||||
|
"KP9" => Some((21.0, r(3), 1.0, 1.0)),
|
||||||
|
"KPLU" => Some((22.0, r(3), 1.0, 2.0)), // +
|
||||||
|
"KP4" => Some((19.0, r(4), 1.0, 1.0)),
|
||||||
|
"KP5" => Some((20.0, r(4), 1.0, 1.0)),
|
||||||
|
"KP6" => Some((21.0, r(4), 1.0, 1.0)),
|
||||||
|
"KP1" => Some((19.0, r(5), 1.0, 1.0)),
|
||||||
|
"KP2" => Some((20.0, r(5), 1.0, 1.0)),
|
||||||
|
"KP3" => Some((21.0, r(5), 1.0, 1.0)),
|
||||||
|
"KP0" => Some((19.0, r(6), 2.0, 1.0)),
|
||||||
|
"KPDL" => Some((21.0, r(6), 1.0, 1.0)), // .
|
||||||
|
"KPEN" => Some((22.0, r(5), 1.0, 2.0)), // Enter
|
||||||
|
"K7" => Some((19.0, r(3), 1.0, 1.0)), // old alias
|
||||||
|
"K8" => Some((20.0, r(3), 1.0, 1.0)),
|
||||||
|
"K9" => Some((21.0, r(3), 1.0, 1.0)),
|
||||||
|
"K4" => Some((19.0, r(4), 1.0, 1.0)),
|
||||||
|
"K5" => Some((20.0, r(4), 1.0, 1.0)),
|
||||||
|
"K6" => Some((21.0, r(4), 1.0, 1.0)),
|
||||||
|
"K1" => Some((19.0, r(5), 1.0, 1.0)),
|
||||||
|
"K2" => Some((20.0, r(5), 1.0, 1.0)),
|
||||||
|
"K3" => Some((21.0, r(5), 1.0, 1.0)),
|
||||||
|
"K0" => Some((19.0, r(6), 2.0, 1.0)),
|
||||||
|
"KDOT" => Some((21.0, r(6), 1.0, 1.0)),
|
||||||
|
"KENT" => Some((22.0, r(5), 1.0, 2.0)),
|
||||||
|
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn keysym_to_label(sym: xkb::Keysym) -> String {
|
||||||
|
let utf = xkb::keysym_to_utf8(sym);
|
||||||
|
if !utf.is_empty() { return utf; }
|
||||||
|
|
||||||
|
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" => "⌫".into(),
|
||||||
|
"Tab" => "⇥".into(),
|
||||||
|
"Return" => "↵".into(),
|
||||||
|
"Caps_Lock" => "⇪".into(),
|
||||||
|
"Shift_L" => "⇧".into(),
|
||||||
|
"Shift_R" => "⇧".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<XkbLayout> {
|
||||||
|
let context = xkb::Context::new(xkb::CONTEXT_NO_FLAGS);
|
||||||
|
let model = if std::path::Path::new("/usr/share/X11/xkb/symbols/").exists() {
|
||||||
|
"pc105"
|
||||||
|
} else {
|
||||||
|
"pc105"
|
||||||
|
};
|
||||||
|
|
||||||
|
let keymap = xkb::Keymap::new_from_names::<str>(
|
||||||
|
&context,
|
||||||
|
"", // rules
|
||||||
|
model, // model
|
||||||
|
layout, // layout
|
||||||
|
if variant.is_empty() { "" } else { variant }, // variant
|
||||||
|
None::<String>, // 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;
|
||||||
|
|
||||||
|
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::<Vec<_>>()
|
||||||
|
.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 labels: Vec<String> = {
|
||||||
|
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() }]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
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; }
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 = visuals.text_color();
|
||||||
|
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.3, font_size),
|
||||||
|
1 => (kx + kw * 0.5, ky + kh * 0.65, 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,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ pub mod theme;
|
|||||||
pub mod buttons;
|
pub mod buttons;
|
||||||
pub mod combobox;
|
pub mod combobox;
|
||||||
pub mod combobox_stil;
|
pub mod combobox_stil;
|
||||||
|
pub mod kle_svg;
|
||||||
pub mod slideshow;
|
pub mod slideshow;
|
||||||
pub mod error_screen;
|
pub mod error_screen;
|
||||||
|
|
||||||
|
|||||||
+3
-53
@@ -160,56 +160,7 @@ fn setup_fonts(ctx: &egui::Context) {
|
|||||||
ctx.set_fonts(fonts);
|
ctx.set_fonts(fonts);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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();
|
|
||||||
// v.faint_bg_color = c_bg_widget();
|
|
||||||
// v.extreme_bg_color = c_sidebar();
|
|
||||||
// v.code_bg_color = if is_dark { Color32::from_rgb(0x18, 0x18, 0x28) } else { Color32::from_rgb(0xEE, 0xEE, 0xF5) };
|
|
||||||
|
|
||||||
// // Metin
|
|
||||||
// v.override_text_color = None;
|
|
||||||
|
|
||||||
// // Kenarlıklar
|
|
||||||
// v.widgets.noninteractive.bg_stroke = Stroke::new(1.0, c_border());
|
|
||||||
// v.widgets.inactive.bg_stroke = Stroke::new(1.0, c_border());
|
|
||||||
// v.widgets.hovered.bg_stroke = Stroke::new(1.5, c_accent());
|
|
||||||
// v.widgets.active.bg_stroke = Stroke::new(2.0, c_accent());
|
|
||||||
|
|
||||||
// // Widget dolguları
|
|
||||||
// v.widgets.noninteractive.bg_fill = c_bg_widget();
|
|
||||||
// v.widgets.inactive.bg_fill = c_bg_widget();
|
|
||||||
// v.widgets.hovered.bg_fill = if is_dark { Color32::from_rgb(0x35, 0x35, 0x55) } else { Color32::from_rgb(0xD5, 0xD5, 0xE5) };
|
|
||||||
// v.widgets.active.bg_fill = c_accent_dim();
|
|
||||||
|
|
||||||
// // Metin renkleri
|
|
||||||
// v.widgets.noninteractive.fg_stroke = Stroke::new(1.0, c_text());
|
|
||||||
// v.widgets.inactive.fg_stroke = Stroke::new(1.0, c_text());
|
|
||||||
// v.widgets.hovered.fg_stroke = Stroke::new(1.5, c_text());
|
|
||||||
// v.widgets.active.fg_stroke = Stroke::new(2.0, Color32::WHITE);
|
|
||||||
|
|
||||||
// // Yuvarlak köşeler
|
|
||||||
// v.widgets.noninteractive.rounding = Rounding::same(6.0);
|
|
||||||
// v.widgets.inactive.rounding = Rounding::same(6.0);
|
|
||||||
// v.widgets.hovered.rounding = Rounding::same(6.0);
|
|
||||||
// v.widgets.active.rounding = Rounding::same(6.0);
|
|
||||||
|
|
||||||
// // Seçim rengi
|
|
||||||
// v.selection.bg_fill = c_accent_dim();
|
|
||||||
// v.selection.stroke = Stroke::new(1.0, c_accent());
|
|
||||||
|
|
||||||
// // Hyperlink
|
|
||||||
// v.hyperlink_color = c_accent();
|
|
||||||
|
|
||||||
// // Pencere kenarlığı
|
|
||||||
// v.window_stroke = Stroke::new(1.0, c_border());
|
|
||||||
// v.window_rounding = Rounding::same(8.0);
|
|
||||||
|
|
||||||
// v
|
|
||||||
// }
|
|
||||||
|
|
||||||
fn build_visuals(is_dark: bool) -> Visuals {
|
fn build_visuals(is_dark: bool) -> Visuals {
|
||||||
let mut v = if is_dark { Visuals::dark() } else { Visuals::light() };
|
let mut v = if is_dark { Visuals::dark() } else { Visuals::light() };
|
||||||
@@ -230,15 +181,14 @@ fn build_visuals(is_dark: bool) -> Visuals {
|
|||||||
// ─── WIDGET DOLGULARI (Burası ComboBox ve Butonların Kaderini Belirliyor) ───────
|
// ─── WIDGET DOLGULARI (Burası ComboBox ve Butonların Kaderini Belirliyor) ───────
|
||||||
v.widgets.noninteractive.bg_fill = c_bg_widget();
|
v.widgets.noninteractive.bg_fill = c_bg_widget();
|
||||||
|
|
||||||
// Pasif Hali: Koyu widget arka planı
|
// Pasif Hali: Widget arka planı (TextEdit, ComboBox giriş alanı)
|
||||||
//v.widgets.inactive.bg_fill = Color32::from_rgb(40, 43, 52);
|
v.widgets.inactive.bg_fill = Color32::from_rgb(255, 255, 255);
|
||||||
//v.widgets.inactive.bg_fill = Color32::from_rgb(224, 224, 224);
|
|
||||||
|
|
||||||
// Üzerine Gelme Hali: Hafif açık
|
// Üzerine Gelme Hali: Hafif açık
|
||||||
v.widgets.hovered.bg_fill = Color32::from_rgb(55, 58, 68);
|
v.widgets.hovered.bg_fill = Color32::from_rgb(55, 58, 68);
|
||||||
|
|
||||||
// Tıklanma/Açık Kalma Hali: Daha açık
|
// Tıklanma/Açık Kalma Hali: Daha açık
|
||||||
v.widgets.active.bg_fill = Color32::from_rgb(60, 63, 75);
|
v.widgets.active.bg_fill = Color32::WHITE;
|
||||||
|
|
||||||
// ComboBox stilleri (combobox_stil.rs)
|
// ComboBox stilleri (combobox_stil.rs)
|
||||||
crate::ui::combobox_stil::apply_visuals(&mut v);
|
crate::ui::combobox_stil::apply_visuals(&mut v);
|
||||||
|
|||||||
Reference in New Issue
Block a user