mirror of
https://gitlab.com/erkanisik/yali-rs.git
synced 2026-07-31 03:09:00 +00:00
feat: keyboard layout/variant search with custom combobox, hex color constants, various UI fixes
- Custom combobox implementation with TextEdit + Area popup for keyboard layout/variant search - Added color_from_hex() helper, layout_buf/variant_buf fields - Removed egui-dropdown dependency - Popup items 28px -> 32px with gradient background - Dropdown text color changed to pure white - All color constants converted to hex format - KLE SVG rendering improvements - CHANGELOG.md updated
This commit is contained in:
+17
-1
@@ -1,6 +1,22 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
## [4.0.2] — 2026-06-21
|
## [4.0.3] — 2026-06-23
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- Aramalı klavye düzeni/varyant seçici: egui `TextEdit` + `Area` popup ile özel implementasyon
|
||||||
|
- `color_from_hex(hex: u32)` yardımcısı (`combobox_stil.rs`)
|
||||||
|
- `layout_buf`, `variant_buf` alanları (`KeyboardStep`)
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- `egui-dropdown` bağımlılığı kaldırıldı (artık kullanılmıyor)
|
||||||
|
- Düzen/Varyant seçiciler yan yana → alt alta, etiket genişliği en uzun etikete göre eşitlendi
|
||||||
|
- Popup öğe yüksekliği 28px → 32px, gradient arkaplan (`draw_gradient_bg`) ile `combobox.rs` stilinde
|
||||||
|
- `DROPDOWN_TEXT_COLOR` kırık beyaz → tam beyaz (`color_from_hex(0xFFFFFF)`)
|
||||||
|
- Tüm renk sabitleri hex formatına çevrildi (`color_from_hex`)
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Popup ilk açılışta eski seçim filtresiyle tüm öğelerin görünmemesi — odaklanınca tampon temizleniyor
|
||||||
|
- `t!()` dönüş tipi `Cow<str>` → `String` uyumsuzluğu (`keyboard.rs`)
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- Sürgü maksimum değeri artık diskteki kullanılabilir alan kadar (diğer bölümler hariç)
|
- Sürgü maksimum değeri artık diskteki kullanılabilir alan kadar (diğer bölümler hariç)
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
|
After Width: | Height: | Size: 40 KiB |
+1
-1
@@ -277,7 +277,7 @@ impl Default for GlobalState {
|
|||||||
install_log: Vec::new(),
|
install_log: Vec::new(),
|
||||||
install_current_file: None,
|
install_current_file: None,
|
||||||
demo_mode: false,
|
demo_mode: false,
|
||||||
is_dark: false,
|
is_dark: true,
|
||||||
rescue_mode: false,
|
rescue_mode: false,
|
||||||
bootloader_device: String::new(),
|
bootloader_device: String::new(),
|
||||||
bootloader_timeout: 5,
|
bootloader_timeout: 5,
|
||||||
|
|||||||
+19
-19
@@ -48,8 +48,8 @@ fn run_gui(demo_mode: bool) -> eframe::Result<()> {
|
|||||||
Box::new(move |cc| {
|
Box::new(move |cc| {
|
||||||
// Görsel yükleyicileri etkinleştir (PNG desteği)
|
// Görsel yükleyicileri etkinleştir (PNG desteği)
|
||||||
egui_extras::install_image_loaders(&cc.egui_ctx);
|
egui_extras::install_image_loaders(&cc.egui_ctx);
|
||||||
ui::theme::set_theme_mode(false);
|
ui::theme::set_theme_mode(true);
|
||||||
ui::theme::apply(&cc.egui_ctx, false);
|
ui::theme::apply(&cc.egui_ctx, true);
|
||||||
let mut app = YaliApp::default();
|
let mut app = YaliApp::default();
|
||||||
app.state.demo_mode = demo_mode;
|
app.state.demo_mode = demo_mode;
|
||||||
Box::new(app)
|
Box::new(app)
|
||||||
@@ -407,24 +407,24 @@ impl eframe::App for YaliApp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Alt kısım: tema değiştirme
|
// Alt kısım: tema değiştirme
|
||||||
// ui.with_layout(egui::Layout::bottom_up(egui::Align::Center), |ui| {
|
ui.with_layout(egui::Layout::bottom_up(egui::Align::Center), |ui| {
|
||||||
// ui.add_space(6.0);
|
ui.add_space(6.0);
|
||||||
|
|
||||||
// let theme_icon = if self.state.is_dark { "☀" } else { "🌙" };
|
let theme_icon = if self.state.is_dark { "☀" } else { "🌙" };
|
||||||
// let theme_label = if self.state.is_dark { "Light" } else { "Dark" };
|
let theme_label = if self.state.is_dark { "Light" } else { "Dark" };
|
||||||
// if ui.add(
|
if ui.add(
|
||||||
// egui::Button::new(
|
egui::Button::new(
|
||||||
// egui::RichText::new(format!("{} {}", theme_icon, theme_label))
|
egui::RichText::new(format!("{} {}", theme_icon, theme_label))
|
||||||
// .size(12.0)
|
.size(12.0)
|
||||||
// )
|
)
|
||||||
// .rounding(egui::Rounding::same(6.0))
|
.rounding(egui::Rounding::same(6.0))
|
||||||
// .min_size(egui::Vec2::new(100.0, 28.0))
|
.min_size(egui::Vec2::new(100.0, 28.0))
|
||||||
// ).clicked() {
|
).clicked() {
|
||||||
// self.state.is_dark = !self.state.is_dark;
|
self.state.is_dark = !self.state.is_dark;
|
||||||
// ui::theme::set_theme_mode(self.state.is_dark);
|
ui::theme::set_theme_mode(self.state.is_dark);
|
||||||
// ui::theme::apply(ctx, self.state.is_dark);
|
ui::theme::apply(ctx, self.state.is_dark);
|
||||||
// }
|
}
|
||||||
// });
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// ── Alt panel: navigasyon ────────────────────────────
|
// ── Alt panel: navigasyon ────────────────────────────
|
||||||
|
|||||||
+142
-41
@@ -1,4 +1,4 @@
|
|||||||
use eframe::egui::{self, Color32};
|
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;
|
use crate::ui::theme;
|
||||||
@@ -92,6 +92,7 @@ fn split_code_desc(line: &str) -> Option<(&str, &str)> {
|
|||||||
Some((code, desc))
|
Some((code, desc))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn layout_display_name(layout: &str, layouts: &[(String, String)]) -> String {
|
fn layout_display_name(layout: &str, layouts: &[(String, String)]) -> String {
|
||||||
layouts
|
layouts
|
||||||
.iter()
|
.iter()
|
||||||
@@ -112,7 +113,6 @@ fn variant_display_name(
|
|||||||
.unwrap_or_else(|| variant.to_string())
|
.unwrap_or_else(|| variant.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn apply_system_keyboard_layout(layout: &str, variant: &str) {
|
fn apply_system_keyboard_layout(layout: &str, variant: &str) {
|
||||||
if std::env::var("DISPLAY").is_ok() {
|
if std::env::var("DISPLAY").is_ok() {
|
||||||
let mut cmd = std::process::Command::new("setxkbmap");
|
let mut cmd = std::process::Command::new("setxkbmap");
|
||||||
@@ -141,7 +141,6 @@ impl InstallerStep for KeyboardStep {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn on_enter(&mut self, state: &mut GlobalState) {
|
fn on_enter(&mut self, state: &mut GlobalState) {
|
||||||
// Varsayılan düzen sistemi diline göre seçilir
|
|
||||||
if state.keyboard_layout.is_empty() {
|
if state.keyboard_layout.is_empty() {
|
||||||
state.keyboard_layout = if state.language == "tr" {
|
state.keyboard_layout = if state.language == "tr" {
|
||||||
"tr".to_string()
|
"tr".to_string()
|
||||||
@@ -162,64 +161,166 @@ impl InstallerStep for KeyboardStep {
|
|||||||
|
|
||||||
ui.add_space(12.0);
|
ui.add_space(12.0);
|
||||||
|
|
||||||
// ── Klavye görseli (xkbcommon ile çizilir, temaya uyumlu) ─
|
// ── Klavye görseli ────────────────────────────────────
|
||||||
if let Some(kle) = crate::ui::kle_svg::compile_layout(&state.keyboard_layout, &state.keyboard_variant) {
|
let 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));
|
.unwrap_or_else(|| crate::ui::kle_svg::empty_layout());
|
||||||
crate::ui::kle_svg::draw_layout(ui, &kle, max);
|
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);
|
ui.add_space(12.0);
|
||||||
|
|
||||||
// ── Layout ve Varyant ComboBox'ları yan yana ──────────────
|
// ── Label genişlik hesaplama ──────────────────────────
|
||||||
let available_variants: Vec<_> = self
|
let layout_label = t!("keyboard_layout_label").to_string();
|
||||||
.variants
|
let variant_label = t!("keyboard_variant_label").to_string();
|
||||||
.iter()
|
let label_w = ui.fonts(|f| {
|
||||||
.filter(|(layout, _, _)| layout == &state.keyboard_layout)
|
let measure = |s: &str| -> f32 {
|
||||||
.collect();
|
f.layout_no_wrap(s.to_string(), egui::FontId::proportional(14.0), egui::Color32::WHITE)
|
||||||
|
.size().x
|
||||||
let current_variant_name = if state.keyboard_variant.is_empty() {
|
};
|
||||||
t!("keyboard_variant_default").to_string()
|
measure(&layout_label).max(measure(&variant_label)).ceil()
|
||||||
} else {
|
}) + 16.0;
|
||||||
let vn = variant_display_name(&state.keyboard_layout, &state.keyboard_variant, &self.variants);
|
|
||||||
if vn.is_empty() { state.keyboard_variant.clone() } else { vn }
|
|
||||||
};
|
|
||||||
let current_layout_name = layout_display_name(&state.keyboard_layout, &self.layouts);
|
|
||||||
|
|
||||||
|
// ── Düzen (Layout) ────────────────────────────────────
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
ui.vertical(|ui| {
|
ui.add_space(6.0);
|
||||||
ui.add_space(6.0);
|
ui.allocate_ui_with_layout(
|
||||||
ui.label(egui::RichText::new(t!("keyboard_layout_label")).strong().color(theme::c_text()));
|
egui::Vec2::new(label_w, ui.available_height()),
|
||||||
});
|
egui::Layout::left_to_right(egui::Align::Center),
|
||||||
|
|ui| {
|
||||||
|
ui.label(egui::RichText::new(&layout_label).strong().color(theme::c_text()));
|
||||||
|
},
|
||||||
|
);
|
||||||
ui.add_space(8.0);
|
ui.add_space(8.0);
|
||||||
let lw = ui.available_width().min(200.0);
|
|
||||||
theme::show_combo_box(ui, "kbd_layout", current_layout_name.as_str(), lw, |ui, w| {
|
let lw = ui.available_width();
|
||||||
|
let current_layout = layout_display_name(&state.keyboard_layout, &self.layouts);
|
||||||
|
|
||||||
|
theme::show_combo_box(ui, "kbd_layout", ¤t_layout, lw, |ui, w| {
|
||||||
|
let mut pressed_char = None;
|
||||||
|
ui.input(|i| {
|
||||||
|
for event in &i.events {
|
||||||
|
if let egui::Event::Text(text) = event {
|
||||||
|
pressed_char = Some(text.to_lowercase());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
for (code, display) in &self.layouts {
|
for (code, display) in &self.layouts {
|
||||||
if theme::selectable_value(ui, &mut state.keyboard_layout, code.clone(), display.as_str(), w).clicked() {
|
let is_selected = state.keyboard_layout == *code;
|
||||||
|
let is_target = if let Some(ref c) = pressed_char {
|
||||||
|
display.to_lowercase().starts_with(c)
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
};
|
||||||
|
|
||||||
|
if is_target {
|
||||||
|
state.keyboard_layout = code.clone();
|
||||||
if !self.variants.iter().any(|(layout, _, _)| layout == code) {
|
if !self.variants.iter().any(|(layout, _, _)| layout == code) {
|
||||||
state.keyboard_variant.clear();
|
state.keyboard_variant.clear();
|
||||||
}
|
}
|
||||||
apply_system_keyboard_layout(&state.keyboard_layout, &state.keyboard_variant);
|
apply_system_keyboard_layout(&state.keyboard_layout, &state.keyboard_variant);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let response = theme::selectable_value(ui, &mut state.keyboard_layout, code.clone(), display.as_str(), w);
|
||||||
|
|
||||||
|
if response.clicked() {
|
||||||
|
if !self.variants.iter().any(|(layout, _, _)| layout == code) {
|
||||||
|
state.keyboard_variant.clear();
|
||||||
|
}
|
||||||
|
apply_system_keyboard_layout(&state.keyboard_layout, &state.keyboard_variant);
|
||||||
|
}
|
||||||
|
|
||||||
|
if is_target || (is_selected && pressed_char.is_none()) {
|
||||||
|
response.scroll_to_me(None);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
ui.add_space(16.0);
|
ui.add_space(8.0);
|
||||||
|
|
||||||
ui.vertical(|ui| {
|
// ── Varyant ───────────────────────────────────────────
|
||||||
ui.add_space(6.0);
|
ui.horizontal(|ui| {
|
||||||
ui.label(egui::RichText::new(t!("keyboard_variant_label")).strong().color(theme::c_text()));
|
ui.add_space(6.0);
|
||||||
});
|
ui.allocate_ui_with_layout(
|
||||||
|
egui::Vec2::new(label_w, ui.available_height()),
|
||||||
|
egui::Layout::left_to_right(egui::Align::Center),
|
||||||
|
|ui| {
|
||||||
|
ui.label(egui::RichText::new(&variant_label).strong().color(theme::c_text()));
|
||||||
|
},
|
||||||
|
);
|
||||||
ui.add_space(8.0);
|
ui.add_space(8.0);
|
||||||
let vw = ui.available_width().min(200.0);
|
|
||||||
theme::show_combo_box(ui, "kbd_variant", current_variant_name.as_str(), vw, |ui, w| {
|
let vw = ui.available_width();
|
||||||
let default_variant = String::new();
|
let available_variants: Vec<(String, String)> = self
|
||||||
if theme::selectable_value(ui, &mut state.keyboard_variant, default_variant, &t!("keyboard_variant_default"), w).clicked() {
|
.variants
|
||||||
|
.iter()
|
||||||
|
.filter(|(layout, _, _)| layout == &state.keyboard_layout)
|
||||||
|
.map(|(_, v_code, display)| (v_code.clone(), display.clone()))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let current_variant = if state.keyboard_variant.is_empty() {
|
||||||
|
t!("keyboard_variant_default").to_string()
|
||||||
|
} else {
|
||||||
|
let vn = variant_display_name(&state.keyboard_layout, &state.keyboard_variant, &self.variants);
|
||||||
|
if vn.is_empty() { state.keyboard_variant.clone() } else { vn }
|
||||||
|
};
|
||||||
|
let default_label = t!("keyboard_variant_default").to_string();
|
||||||
|
|
||||||
|
theme::show_combo_box(ui, "kbd_variant", ¤t_variant, vw, |ui, w| {
|
||||||
|
let mut pressed_char = None;
|
||||||
|
ui.input(|i| {
|
||||||
|
for event in &i.events {
|
||||||
|
if let egui::Event::Text(text) = event {
|
||||||
|
pressed_char = Some(text.to_lowercase());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Varsayılan Varyant Elemanı
|
||||||
|
let is_default_selected = state.keyboard_variant.is_empty();
|
||||||
|
let is_default_target = if let Some(ref c) = pressed_char {
|
||||||
|
default_label.to_lowercase().starts_with(c)
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
};
|
||||||
|
|
||||||
|
if is_default_target {
|
||||||
|
state.keyboard_variant.clear();
|
||||||
apply_system_keyboard_layout(&state.keyboard_layout, "");
|
apply_system_keyboard_layout(&state.keyboard_layout, "");
|
||||||
}
|
}
|
||||||
for (_, variant, display) in &available_variants {
|
|
||||||
if theme::selectable_value(ui, &mut state.keyboard_variant, variant.clone(), display.as_str(), w).clicked() {
|
let def_response = theme::selectable_value(ui, &mut state.keyboard_variant, String::new(), &default_label, w);
|
||||||
|
if def_response.clicked() {
|
||||||
|
apply_system_keyboard_layout(&state.keyboard_layout, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
if is_default_target || (is_default_selected && pressed_char.is_none()) {
|
||||||
|
def_response.scroll_to_me(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Diğer Varyantlar
|
||||||
|
for (v_code, display) in &available_variants {
|
||||||
|
let is_selected = state.keyboard_variant == *v_code;
|
||||||
|
let is_target = if let Some(ref c) = pressed_char {
|
||||||
|
display.to_lowercase().starts_with(c)
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
};
|
||||||
|
|
||||||
|
if is_target {
|
||||||
|
state.keyboard_variant = v_code.clone();
|
||||||
apply_system_keyboard_layout(&state.keyboard_layout, &state.keyboard_variant);
|
apply_system_keyboard_layout(&state.keyboard_layout, &state.keyboard_variant);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let response = theme::selectable_value(ui, &mut state.keyboard_variant, v_code.clone(), display.as_str(), w);
|
||||||
|
if response.clicked() {
|
||||||
|
apply_system_keyboard_layout(&state.keyboard_layout, &state.keyboard_variant);
|
||||||
|
}
|
||||||
|
|
||||||
|
if is_target || (is_selected && pressed_char.is_none()) {
|
||||||
|
response.scroll_to_me(None);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -228,7 +329,7 @@ impl InstallerStep for KeyboardStep {
|
|||||||
ui.label(t!("keyboard_test_label"));
|
ui.label(t!("keyboard_test_label"));
|
||||||
ui.add_space(4.0);
|
ui.add_space(4.0);
|
||||||
egui::Frame::group(ui.style())
|
egui::Frame::group(ui.style())
|
||||||
.fill(Color32::from_rgb(255, 255, 255))
|
.fill(theme::c_keyboard_bg())
|
||||||
.rounding(6.0)
|
.rounding(6.0)
|
||||||
.show(ui, |ui| {
|
.show(ui, |ui| {
|
||||||
ui.add(
|
ui.add(
|
||||||
|
|||||||
+8
-7
@@ -48,13 +48,14 @@ pub fn selectable_value<T: PartialEq>(
|
|||||||
|
|
||||||
draw_gradient_bg(ui, rect);
|
draw_gradient_bg(ui, rect);
|
||||||
|
|
||||||
ui.put(rect, |ui: &mut egui::Ui| {
|
let painter = ui.painter();
|
||||||
ui.horizontal(|ui| {
|
painter.text(
|
||||||
ui.add_space(6.0);
|
egui::pos2(rect.left() + 6.0, rect.center().y),
|
||||||
ui.colored_label(DROPDOWN_TEXT_COLOR, label);
|
egui::Align2::LEFT_CENTER,
|
||||||
})
|
label,
|
||||||
.response
|
egui::FontId::proportional(14.0),
|
||||||
});
|
DROPDOWN_TEXT_COLOR,
|
||||||
|
);
|
||||||
|
|
||||||
if response.clicked() {
|
if response.clicked() {
|
||||||
*current = value;
|
*current = value;
|
||||||
|
|||||||
+112
-176
@@ -1,4 +1,4 @@
|
|||||||
use eframe::egui::{self, Rect, Rounding, Stroke, Vec2};
|
use eframe::egui::{self, Vec2};
|
||||||
use xkbcommon::xkb;
|
use xkbcommon::xkb;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
@@ -10,36 +10,16 @@ pub struct XkbKey {
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct XkbLayout {
|
pub struct XkbLayout {
|
||||||
pub keys: Vec<XkbKey>,
|
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.
|
pub fn empty_layout() -> XkbLayout {
|
||||||
/// x,y ondalıklı olabilir (geniş tuşlar için).
|
XkbLayout { keys: vec![] }
|
||||||
|
}
|
||||||
|
|
||||||
fn key_position(name: &str) -> Option<(f32, f32, f32, f32)> {
|
fn key_position(name: &str) -> Option<(f32, f32, f32, f32)> {
|
||||||
// Satır y offset'leri (birim)
|
|
||||||
let r = |row| row as f32;
|
let r = |row| row as f32;
|
||||||
|
|
||||||
// Standart ISO‑105 düzeni. x değerleri kümülatif.
|
|
||||||
match name {
|
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)),
|
"TLDE" => Some((0.0, r(1), 1.0, 1.0)),
|
||||||
"AE01" => Some((1.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)),
|
"AE02" => Some((2.0, r(1), 1.0, 1.0)),
|
||||||
@@ -53,9 +33,8 @@ fn key_position(name: &str) -> Option<(f32, f32, f32, f32)> {
|
|||||||
"AE10" => Some((10.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)),
|
"AE11" => Some((11.0, r(1), 1.0, 1.0)),
|
||||||
"AE12" => Some((12.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)),
|
"BKSP" => Some((13.0, r(1), 2.25, 1.0)),
|
||||||
|
|
||||||
// ── Top alpha row (Q–P) ────────
|
|
||||||
"TAB" => Some((0.0, r(2), 1.5, 1.0)),
|
"TAB" => Some((0.0, r(2), 1.5, 1.0)),
|
||||||
"AD01" => Some((1.5, r(2), 1.0, 1.0)),
|
"AD01" => Some((1.5, r(2), 1.0, 1.0)),
|
||||||
"AD02" => Some((2.5, r(2), 1.0, 1.0)),
|
"AD02" => Some((2.5, r(2), 1.0, 1.0)),
|
||||||
@@ -69,10 +48,7 @@ fn key_position(name: &str) -> Option<(f32, f32, f32, f32)> {
|
|||||||
"AD10" => Some((10.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)),
|
"AD11" => Some((11.5, r(2), 1.0, 1.0)),
|
||||||
"AD12" => Some((12.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)),
|
"CAPS" => Some((0.0, r(3), 1.75, 1.0)),
|
||||||
"AC01" => Some((1.75, r(3), 1.0, 1.0)),
|
"AC01" => Some((1.75, r(3), 1.0, 1.0)),
|
||||||
"AC02" => Some((2.75, r(3), 1.0, 1.0)),
|
"AC02" => Some((2.75, r(3), 1.0, 1.0)),
|
||||||
@@ -86,10 +62,10 @@ fn key_position(name: &str) -> Option<(f32, f32, f32, f32)> {
|
|||||||
"AC10" => Some((10.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)),
|
"AC11" => Some((11.75,r(3), 1.0, 1.0)),
|
||||||
"AC12" => Some((12.75,r(3), 1.0, 1.0)),
|
"AC12" => Some((12.75,r(3), 1.0, 1.0)),
|
||||||
|
"BKSL" => Some((12.50,r(3), 1.0, 1.0)),
|
||||||
|
|
||||||
// ── Bottom row (Z–M) ──────────
|
|
||||||
"LFSH" => Some((0.0, r(4), 1.25, 1.0)),
|
"LFSH" => Some((0.0, r(4), 1.25, 1.0)),
|
||||||
"LSGT" => Some((1.25, r(4), 1.0, 1.0)), // ISO extra (< > |)
|
"LSGT" => Some((1.25, r(4), 1.0, 1.0)),
|
||||||
"AB01" => Some((2.25, r(4), 1.0, 1.0)),
|
"AB01" => Some((2.25, r(4), 1.0, 1.0)),
|
||||||
"AB02" => Some((3.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)),
|
"AB03" => Some((4.25, r(4), 1.0, 1.0)),
|
||||||
@@ -100,61 +76,15 @@ fn key_position(name: &str) -> Option<(f32, f32, f32, f32)> {
|
|||||||
"AB08" => Some((9.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)),
|
"AB09" => Some((10.25,r(4), 1.0, 1.0)),
|
||||||
"AB10" => Some((11.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((12.25,r(4), 3.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)),
|
"LCTL" => Some((0.0, r(5), 1.25, 1.0)),
|
||||||
"LWIN" => Some((1.25, 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)),
|
"LALT" => Some((2.5, r(5), 1.5, 1.0)),
|
||||||
"SPCE" => Some((4.0, r(5), 6.25, 1.0)),
|
"SPCE" => Some((4.0, r(5), 6.25, 1.0)),
|
||||||
"RALT" | "LVL3" => Some((10.25,r(5), 1.5, 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((11.75, r(5), 1.25, 1.0)),
|
||||||
"COMP" => Some((13.0, r(5), 1.25, 1.0)),
|
"RCTL" => Some((13.0, r(5), 2.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,
|
_ => None,
|
||||||
}
|
}
|
||||||
@@ -162,94 +92,76 @@ fn key_position(name: &str) -> Option<(f32, f32, f32, f32)> {
|
|||||||
|
|
||||||
fn keysym_to_label(sym: xkb::Keysym) -> String {
|
fn keysym_to_label(sym: xkb::Keysym) -> String {
|
||||||
let utf = xkb::keysym_to_utf8(sym);
|
let utf = xkb::keysym_to_utf8(sym);
|
||||||
if !utf.is_empty() { return utf; }
|
if utf.chars().any(|c| !c.is_control()) {
|
||||||
|
let trimmed = utf.trim();
|
||||||
let name = xkb::keysym_get_name(sym);
|
if !trimmed.is_empty() {
|
||||||
if name == "VoidSymbol" || name == "NoSymbol" {
|
return trimmed.to_uppercase();
|
||||||
return String::new();
|
}
|
||||||
}
|
}
|
||||||
|
let name = xkb::keysym_get_name(sym);
|
||||||
// Yaygın XKB sembol adlarını kısa etiketlere çevir
|
if name == "VoidSymbol" || name == "NoSymbol" { return String::new(); }
|
||||||
match name.as_str() {
|
match name.as_str() {
|
||||||
"BackSpace" => "⌫".into(),
|
"BackSpace" => "Bksp".into(),
|
||||||
"Tab" => "⇥".into(),
|
"Tab" => "Tab".into(),
|
||||||
"Return" => "↵".into(),
|
"Return" => "Enter".into(),
|
||||||
"Caps_Lock" => "⇪".into(),
|
"Caps_Lock" => "Caps".into(),
|
||||||
"Shift_L" => "⇧".into(),
|
"Shift_L" | "Shift_R" => "Shift".into(),
|
||||||
"Shift_R" => "⇧".into(),
|
"Control_L" | "Control_R" => "Ctrl".into(),
|
||||||
"Control_L" => "Ctrl".into(),
|
|
||||||
"Control_R" => "Ctrl".into(),
|
|
||||||
"Alt_L" => "Alt".into(),
|
"Alt_L" => "Alt".into(),
|
||||||
"Alt_R" => "AltGr".into(),
|
"Alt_R" | "ISO_Level3_Shift" => "AltGr".into(),
|
||||||
"ISO_Level3_Shift" => "AltGr".into(),
|
"Super_L" | "Super_R" => "Win".into(),
|
||||||
"Super_L" => "Win".into(),
|
|
||||||
"Super_R" => "Win".into(),
|
|
||||||
"Menu" => "Menü".into(),
|
"Menu" => "Menü".into(),
|
||||||
"Escape" => "Esc".into(),
|
"Escape" => "Esc".into(),
|
||||||
"Delete" => "Del".into(),
|
|
||||||
"Insert" => "Ins".into(),
|
|
||||||
"Prior" => "PgUp".into(),
|
|
||||||
"Next" => "PgDn".into(),
|
|
||||||
"space" => "".into(),
|
"space" => "".into(),
|
||||||
"Multi_key" => "Compose".into(),
|
|
||||||
_ if name.starts_with("XF86") => String::new(),
|
|
||||||
_ => name,
|
_ => name,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// XKB keymap'inden klavye düzenini oluşturur.
|
|
||||||
pub fn compile_layout(layout: &str, variant: &str) -> Option<XkbLayout> {
|
pub fn compile_layout(layout: &str, variant: &str) -> Option<XkbLayout> {
|
||||||
let context = xkb::Context::new(xkb::CONTEXT_NO_FLAGS);
|
let mut context = xkb::Context::new(xkb::CONTEXT_NO_FLAGS);
|
||||||
let model = if std::path::Path::new("/usr/share/X11/xkb/symbols/").exists() {
|
context.set_log_level(xkb::LogLevel::Critical);
|
||||||
"pc105"
|
|
||||||
} else {
|
|
||||||
"pc105"
|
|
||||||
};
|
|
||||||
|
|
||||||
let keymap = xkb::Keymap::new_from_names::<str>(
|
let keymap = xkb::Keymap::new_from_names::<str>(
|
||||||
&context,
|
&context, "", "pc105", layout,
|
||||||
"", // rules
|
if variant.is_empty() { "" } else { variant },
|
||||||
model, // model
|
None::<String>, xkb::COMPILE_NO_FLAGS,
|
||||||
layout, // layout
|
|
||||||
if variant.is_empty() { "" } else { variant }, // variant
|
|
||||||
None::<String>, // options
|
|
||||||
xkb::COMPILE_NO_FLAGS,
|
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let min = keymap.min_keycode();
|
let min = keymap.min_keycode();
|
||||||
let max = keymap.max_keycode();
|
let max = keymap.max_keycode();
|
||||||
let mut keys = Vec::new();
|
let mut keys = Vec::new();
|
||||||
let mut min_x = f32::MAX;
|
|
||||||
let mut min_y = f32::MAX;
|
let mut min_y = f32::MAX;
|
||||||
let mut max_x = f32::MIN;
|
|
||||||
let mut max_y = f32::MIN;
|
let mut enter_label = "Enter".to_string();
|
||||||
|
|
||||||
for code_u32 in (min.raw() as u32)..=(max.raw() as u32) {
|
for code_u32 in (min.raw() as u32)..=(max.raw() as u32) {
|
||||||
let code = xkb::Keycode::new(code_u32);
|
let code = xkb::Keycode::new(code_u32);
|
||||||
let Some(name) = keymap.key_get_name(code) else { continue };
|
let Some(name) = keymap.key_get_name(code) else { continue };
|
||||||
|
|
||||||
|
if name == "RTRN" {
|
||||||
|
let syms = keymap.key_get_syms_by_level(code, 0, 0);
|
||||||
|
let label = syms.iter().map(|&s| keysym_to_label(s)).collect::<Vec<_>>().join("");
|
||||||
|
if !label.is_empty() { enter_label = label; }
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
let Some((x, y, w, h)) = key_position(name) 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();
|
let mut raw_labels = Vec::new();
|
||||||
for level in 0..4 {
|
for level in 0..4 {
|
||||||
let syms = keymap.key_get_syms_by_level(code, 0, level);
|
let syms = keymap.key_get_syms_by_level(code, 0, level);
|
||||||
let label = syms.iter()
|
let label = syms.iter().map(|&s| keysym_to_label(s)).collect::<Vec<_>>().join("");
|
||||||
.map(|&s| keysym_to_label(s))
|
|
||||||
.collect::<Vec<_>>()
|
|
||||||
.join("");
|
|
||||||
if !label.is_empty() && !raw_labels.contains(&label) {
|
if !label.is_empty() && !raw_labels.contains(&label) {
|
||||||
raw_labels.push(label);
|
raw_labels.push(label);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Her tuşta sadece 1 karakter göster (büyük harf tercihli)
|
|
||||||
let labels: Vec<String> = {
|
let labels: Vec<String> = {
|
||||||
let single: Vec<_> = raw_labels.iter().filter(|l| l.chars().count() == 1).collect();
|
let single: Vec<_> = raw_labels.iter().filter(|l| l.chars().count() == 1).collect();
|
||||||
if single.is_empty() {
|
if single.is_empty() {
|
||||||
// Hiç tek karakter yoksa ilk label'ı göster (F1, Esc, …)
|
|
||||||
raw_labels.into_iter().take(1).collect()
|
raw_labels.into_iter().take(1).collect()
|
||||||
} else {
|
} 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()));
|
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() }]
|
vec![if let Some(s) = uc { (*s).clone() } else { (*single[0]).clone() }]
|
||||||
}
|
}
|
||||||
@@ -257,78 +169,102 @@ pub fn compile_layout(layout: &str, variant: &str) -> Option<XkbLayout> {
|
|||||||
|
|
||||||
keys.push(XkbKey { x, y, w, h, labels });
|
keys.push(XkbKey { x, y, w, h, labels });
|
||||||
|
|
||||||
if x < min_x { min_x = x; }
|
|
||||||
if y < min_y { min_y = y; }
|
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
|
keys.push(XkbKey { x: 13.5, y: 2.0, w: 1.75, h: 1.0, labels: vec![enter_label.clone()] });
|
||||||
|
keys.push(XkbKey { x: 14.75, y: 3.0, w: 0.5, h: 1.0, labels: vec!["_RTRN_BOT".to_string()] });
|
||||||
|
|
||||||
for key in &mut keys {
|
for key in &mut keys {
|
||||||
key.x -= min_x;
|
|
||||||
key.y -= min_y;
|
key.y -= min_y;
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(XkbLayout {
|
Some(XkbLayout {
|
||||||
keys,
|
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) {
|
pub fn draw_layout(ui: &mut egui::Ui, layout: &XkbLayout, max_size: Vec2) {
|
||||||
let margin = 6.0;
|
let margin = 6.0;
|
||||||
let spacing = 0.08;
|
|
||||||
let corner = 3.0;
|
|
||||||
|
|
||||||
let uw = (max_size.x - margin * 2.0) / layout.width;
|
let svg_size = Vec2::new(840.0, 300.0);
|
||||||
let uh = (max_size.y - margin * 2.0) / layout.height;
|
let img_scale = ((max_size.x - margin * 2.0) / svg_size.x)
|
||||||
let unit = uw.min(uh).max(12.0);
|
.min((max_size.y - margin * 2.0) / svg_size.y)
|
||||||
|
.max(0.1);
|
||||||
|
let draw_size = svg_size * img_scale;
|
||||||
|
let total_size = Vec2::new(draw_size.x + margin * 2.0, draw_size.y + margin * 2.0);
|
||||||
|
|
||||||
let draw_w = layout.width * unit;
|
let (_id, resp) = ui.allocate_exact_size(total_size, egui::Sense::hover());
|
||||||
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 painter = ui.painter_at(resp.rect);
|
||||||
let origin = resp.rect.min;
|
let origin = egui::pos2(resp.rect.min.x + margin, resp.rect.min.y + margin);
|
||||||
|
|
||||||
let visuals = ui.visuals();
|
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();
|
let bg = visuals.window_fill();
|
||||||
|
let is_dark = visuals.dark_mode;
|
||||||
|
|
||||||
painter.rect_filled(resp.rect, Rounding::same(6.0), bg);
|
painter.rect_filled(resp.rect, egui::Rounding::same(6.0), bg);
|
||||||
|
|
||||||
let font_size = (unit * 0.28).max(6.0);
|
let img_rect = egui::Rect::from_min_size(origin, draw_size);
|
||||||
let small_size = (unit * 0.22).max(5.0);
|
egui::Image::from_bytes(
|
||||||
|
"keyboard.svg",
|
||||||
|
&include_bytes!("../../assets/keyboard-layout.svg")[..],
|
||||||
|
).paint_at(ui, img_rect);
|
||||||
|
|
||||||
let sp = unit * spacing;
|
// Dark mode: SVG'yi karart
|
||||||
|
if is_dark {
|
||||||
|
painter.rect_filled(img_rect, egui::Rounding::same(4.0), egui::Color32::from_black_alpha(170));
|
||||||
|
}
|
||||||
|
|
||||||
|
let font_size = (draw_size.x * 0.015).max(6.0);
|
||||||
|
|
||||||
|
let text_color = if is_dark { egui::Color32::from_gray(230) } else { egui::Color32::from_gray(30) };
|
||||||
|
|
||||||
|
const PITCH: f32 = 54.0;
|
||||||
|
const KEY_PX: f32 = 52.0;
|
||||||
|
const CX0: f32 = 42.0;
|
||||||
|
const CY0: f32 = 42.0;
|
||||||
|
|
||||||
|
const ENTER_VBX: f32 = 791.0;
|
||||||
|
const ENTER_VBY: f32 = 123.0;
|
||||||
|
|
||||||
|
let mut enter_text = String::new();
|
||||||
|
|
||||||
for key in &layout.keys {
|
for key in &layout.keys {
|
||||||
let kx = origin.x + margin + key.x * unit + sp;
|
if let Some(lbl) = key.labels.first() {
|
||||||
let ky = origin.y + margin + key.y * unit + sp;
|
if lbl == "_RTRN_BOT" { continue; }
|
||||||
let kw = key.w * unit - sp * 2.0;
|
if key.y == 1.0 && (key.x - 13.5).abs() < 0.1 {
|
||||||
let kh = key.h * unit - sp * 2.0;
|
enter_text = lbl.clone();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if kw <= 0.0 || kh <= 0.0 { continue; }
|
let svg_cx = CX0 + key.x * PITCH;
|
||||||
|
let svg_cy = CY0 + key.y * PITCH;
|
||||||
|
let svg_w = KEY_PX + (key.w - 1.0) * PITCH;
|
||||||
|
let svg_h = KEY_PX + (key.h - 1.0) * PITCH;
|
||||||
|
|
||||||
let key_rect = Rect::from_min_size(egui::pos2(kx, ky), Vec2::new(kw, kh));
|
let ex = origin.x + (svg_cx - svg_w / 2.0) * img_scale;
|
||||||
painter.rect(key_rect, Rounding::same(corner), key_fill, Stroke::new(key_stroke.width.max(0.5), key_stroke.color));
|
let ey = origin.y + (svg_cy - svg_h / 2.0) * img_scale;
|
||||||
|
let ew = svg_w * img_scale;
|
||||||
|
let eh = svg_h * img_scale;
|
||||||
|
|
||||||
for (i, label) in key.labels.iter().enumerate() {
|
if let Some(lbl) = key.labels.first() {
|
||||||
let lbl = label.trim();
|
let trimmed = lbl.trim();
|
||||||
if lbl.is_empty() { continue; }
|
if trimmed.is_empty() { continue; }
|
||||||
let (lx, ly, sz) = match i {
|
|
||||||
0 => (kx + kw * 0.5, ky + kh * 0.3, font_size),
|
let text_pos = egui::pos2(ex + ew / 2.0, ey + eh / 2.0);
|
||||||
1 => (kx + kw * 0.5, ky + kh * 0.65, small_size),
|
|
||||||
_ => (kx + kw * 0.5, ky + kh * 0.9, small_size),
|
painter.text(text_pos, egui::Align2::CENTER_CENTER, trimmed, egui::FontId::proportional(font_size), text_color);
|
||||||
};
|
|
||||||
painter.text(
|
|
||||||
egui::pos2(lx, ly), egui::Align2::CENTER_CENTER,
|
|
||||||
lbl, egui::FontId::proportional(sz), text_color,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !enter_text.is_empty() {
|
||||||
|
painter.text(
|
||||||
|
egui::pos2(origin.x + ENTER_VBX * img_scale, origin.y + ENTER_VBY * img_scale),
|
||||||
|
egui::Align2::CENTER_CENTER,
|
||||||
|
&enter_text,
|
||||||
|
egui::FontId::proportional(font_size),
|
||||||
|
text_color,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+8
-2
@@ -15,6 +15,7 @@ use std::sync::atomic::{AtomicU32, Ordering};
|
|||||||
static BG_DARK: AtomicU32 = AtomicU32::new(0x121214);
|
static BG_DARK: AtomicU32 = AtomicU32::new(0x121214);
|
||||||
static BG_PANEL: AtomicU32 = AtomicU32::new(0x1A1A1E);
|
static BG_PANEL: AtomicU32 = AtomicU32::new(0x1A1A1E);
|
||||||
static BG_WIDGET: AtomicU32 = AtomicU32::new(0x242428);
|
static BG_WIDGET: AtomicU32 = AtomicU32::new(0x242428);
|
||||||
|
static BG_FOOTER: AtomicU32 = AtomicU32::new(0x121214);
|
||||||
|
|
||||||
|
|
||||||
static BORDER: AtomicU32 = AtomicU32::new(0x323238);
|
static BORDER: AtomicU32 = AtomicU32::new(0x323238);
|
||||||
@@ -26,6 +27,7 @@ static ERROR: AtomicU32 = AtomicU32::new(0xF44336);
|
|||||||
static SIDEBAR: AtomicU32 = AtomicU32::new(0x0B0B0D);
|
static SIDEBAR: AtomicU32 = AtomicU32::new(0x0B0B0D);
|
||||||
static ACCENT_COLOR: AtomicU32 = AtomicU32::new(0xd9005b); // Pisi Premium Vurgu (#d9005b)
|
static ACCENT_COLOR: AtomicU32 = AtomicU32::new(0xd9005b); // Pisi Premium Vurgu (#d9005b)
|
||||||
static SIDEBAR_TEXT: AtomicU32 = AtomicU32::new(0x000000);
|
static SIDEBAR_TEXT: AtomicU32 = AtomicU32::new(0x000000);
|
||||||
|
static KEYBOARD_BG: AtomicU32 = AtomicU32::new(0xFFFFFF);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -46,11 +48,13 @@ pub fn c_warning() -> Color32 { color_from_hex(WARNING.load(Ordering::Relaxed)
|
|||||||
pub fn c_error() -> Color32 { color_from_hex(ERROR.load(Ordering::Relaxed)) }
|
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() -> 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_sidebar_text() -> Color32 { color_from_hex(SIDEBAR_TEXT.load(Ordering::Relaxed)) }
|
||||||
pub fn c_footer_bg() -> Color32 { color_from_hex(0xf7f7f7) }
|
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(0xf7f7f7) }
|
||||||
|
pub fn c_keyboard_bg() -> Color32 { color_from_hex(KEYBOARD_BG.load(Ordering::Relaxed)) }
|
||||||
|
|
||||||
pub fn set_theme_mode(is_dark: bool) {
|
pub fn set_theme_mode(is_dark: bool) {
|
||||||
if is_dark {
|
if is_dark {
|
||||||
|
BG_FOOTER.store(0x121214, Ordering::Relaxed);
|
||||||
BG_DARK.store(0x121214, Ordering::Relaxed);
|
BG_DARK.store(0x121214, Ordering::Relaxed);
|
||||||
BG_PANEL.store(0x1A1A1E, Ordering::Relaxed);
|
BG_PANEL.store(0x1A1A1E, Ordering::Relaxed);
|
||||||
BG_WIDGET.store(0x242428, Ordering::Relaxed);
|
BG_WIDGET.store(0x242428, Ordering::Relaxed);
|
||||||
@@ -58,13 +62,15 @@ pub fn set_theme_mode(is_dark: bool) {
|
|||||||
TEXT.store(0xF3F3F5, Ordering::Relaxed);
|
TEXT.store(0xF3F3F5, Ordering::Relaxed);
|
||||||
TEXT_DIM.store(0x9E9EAF, Ordering::Relaxed);
|
TEXT_DIM.store(0x9E9EAF, Ordering::Relaxed);
|
||||||
SIDEBAR.store(0x0B0B0D, Ordering::Relaxed);
|
SIDEBAR.store(0x0B0B0D, Ordering::Relaxed);
|
||||||
|
KEYBOARD_BG.store(0x000000, Ordering::Relaxed);
|
||||||
} else {
|
} else {
|
||||||
|
BG_FOOTER.store(0xf7f7f7, Ordering::Relaxed);
|
||||||
BG_DARK.store(0xf7f7f7, Ordering::Relaxed);
|
BG_DARK.store(0xf7f7f7, Ordering::Relaxed);
|
||||||
SIDEBAR.store(0x292f34, Ordering::Relaxed);
|
SIDEBAR.store(0x292f34, Ordering::Relaxed);
|
||||||
TEXT.store(0x000000, Ordering::Relaxed);
|
TEXT.store(0x000000, Ordering::Relaxed);
|
||||||
TEXT_DIM.store(0x000000, Ordering::Relaxed);
|
TEXT_DIM.store(0x000000, Ordering::Relaxed);
|
||||||
SIDEBAR_TEXT.store(0xffffff, Ordering::Relaxed);
|
SIDEBAR_TEXT.store(0xffffff, Ordering::Relaxed);
|
||||||
|
KEYBOARD_BG.store(0xffffff, Ordering::Relaxed);
|
||||||
|
|
||||||
// BG_DARK.store(0xF0F0F5, Ordering::Relaxed);
|
// BG_DARK.store(0xF0F0F5, Ordering::Relaxed);
|
||||||
// BG_PANEL.store(0xFFFFFF, Ordering::Relaxed);
|
// BG_PANEL.store(0xFFFFFF, Ordering::Relaxed);
|
||||||
|
|||||||
Reference in New Issue
Block a user