diff --git a/src/funct.rs b/src/funct.rs index 459500b..fadb54b 100644 --- a/src/funct.rs +++ b/src/funct.rs @@ -114,21 +114,35 @@ pub fn language_selector_ui(ui: &mut eframe::egui::Ui, state: &mut crate::instal let item_height = 28.0; // Eleman kutularını sarmalayarak tam genişlikte tıklanabilir yapıyoruz - ui.add_sized([available_width, item_height], |ui: &mut egui::Ui| { + let (rect, item_resp) = ui.allocate_exact_size( + egui::vec2(available_width, item_height), + egui::Sense::click(), + ); + { + use egui::epaint::Mesh; + let top = egui::Color32::from_rgb(249, 249, 249); + let bot = egui::Color32::from_rgb(233, 233, 233); + let mut mesh = Mesh::default(); + let idx = mesh.vertices.len() as u32; + mesh.vertices.push(egui::epaint::Vertex { pos: egui::pos2(rect.left(), rect.top()), uv: egui::pos2(0.0, 0.0), color: top }); + mesh.vertices.push(egui::epaint::Vertex { pos: egui::pos2(rect.right(), rect.top()), uv: egui::pos2(0.0, 0.0), color: top }); + mesh.vertices.push(egui::epaint::Vertex { pos: egui::pos2(rect.right(), rect.bottom()), uv: egui::pos2(0.0, 0.0), color: bot }); + mesh.vertices.push(egui::epaint::Vertex { pos: egui::pos2(rect.left(), rect.bottom()), uv: egui::pos2(0.0, 0.0), color: bot }); + mesh.indices.extend_from_slice(&[idx, idx + 1, idx + 2, idx + 2, idx + 3, idx]); + ui.painter().add(egui::Shape::mesh(mesh)); + } + ui.put(rect, |ui: &mut egui::Ui| { ui.horizontal(|ui| { - ui.add_space(6.0); // İç sol boşluk + ui.add_space(6.0); ui.add(eframe::egui::Image::new(image_source.clone()).max_width(18.0)); ui.add_space(4.0); - - let mut current_lang = state.language.clone(); - let resp = ui.selectable_value(&mut current_lang, code.to_string(), *label); - - if resp.clicked() { - state.language = code.to_string(); - } - resp + ui.label(*label); }).response }); + if item_resp.clicked() { + state.language = code.to_string(); + } + item_resp.on_hover_cursor(egui::CursorIcon::PointingHand); } }); }); diff --git a/src/installer.rs b/src/installer.rs index d8d855b..e37de26 100644 --- a/src/installer.rs +++ b/src/installer.rs @@ -471,18 +471,18 @@ impl InstallerStep for WelcomeStep { // Rescue mode butonu ui.add_space(10.0); - ui.allocate_ui( - egui::vec2(ui.available_width(), 30.0), // Genişlik tam, Yükseklik sadece 24px - |ui| { - // Şimdi dikeyde ortalayarak soldan sağa diziyoruz - ui.with_layout(egui::Layout::left_to_right(egui::Align::Center), |ui| { + egui::Frame::none() + .rounding(egui::Rounding::same(6.0)) + .inner_margin(egui::Margin::symmetric(8.0, 4.0)) + .show(ui, |ui| { + ui.horizontal(|ui| { let rescue_available = detect_existing_linux(); if rescue_available { - if theme::secondary_button(ui, &t!("rescue_button")).clicked() { + if theme::light_gray_btn(ui, &t!("rescue_button")).clicked() { state.rescue_mode = true; } - if theme::secondary_button(ui, &t!("recheck")).clicked() { + if theme::light_gray_btn(ui, &t!("recheck")).clicked() { self.checked = false; self.on_enter(state); } @@ -494,8 +494,7 @@ impl InstallerStep for WelcomeStep { ); } }); - } - ); + }); diff --git a/src/ui/buttons.rs b/src/ui/buttons.rs new file mode 100644 index 0000000..819df12 --- /dev/null +++ b/src/ui/buttons.rs @@ -0,0 +1,138 @@ +use eframe::egui::{self, Color32, FontId, Rounding, Stroke, Vec2}; + +fn lerp_color(a: Color32, b: Color32, t: f32) -> Color32 { + Color32::from_rgba_premultiplied( + (a.r() as f32 + (b.r() as f32 - a.r() as f32) * t) as u8, + (a.g() as f32 + (b.g() as f32 - a.g() as f32) * t) as u8, + (a.b() as f32 + (b.b() as f32 - a.b() as f32) * t) as u8, + (a.a() as f32 + (b.a() as f32 - a.a() as f32) * t) as u8, + ) +} + +fn gradient_button(ui: &mut egui::Ui, text: &str, top_color: Color32, bottom_color: Color32, text_color: Color32, hover_top: Color32, hover_bottom: Color32, border_color: Color32) -> egui::Response { + let rounding = Rounding::same(6.0); + let min_size = Vec2::new(100.0, 32.0); + let padding = Vec2::new(12.0, 6.0); + + let font_id = ui.style().text_styles.get(&egui::TextStyle::Button) + .cloned().unwrap_or(FontId::proportional(14.0)); + let galley = ui.painter().layout_no_wrap( + text.to_string(), + font_id, + text_color, + ); + + let size = Vec2::new( + min_size.x.max(galley.size().x + 2.0 * padding.x), + min_size.y.max(galley.size().y + 2.0 * padding.y), + ); + + let (rect, response) = ui.allocate_exact_size(size, egui::Sense::click()); + + let n_strips = 16; + for i in 0..n_strips { + let t = i as f32 / n_strips as f32; + let t2 = (i + 1) as f32 / n_strips as f32; + let c = lerp_color(top_color, bottom_color, (t + t2) * 0.5); + let y0 = rect.top() + rect.height() * t; + let y1 = rect.top() + rect.height() * t2; + let strip_rect = egui::Rect::from_min_max( + egui::pos2(rect.left(), y0), + egui::pos2(rect.right(), y1), + ); + ui.painter().rect(strip_rect, Rounding::ZERO, c, Stroke::NONE); + } + + if response.hovered() { + for i in 0..n_strips { + let t = i as f32 / n_strips as f32; + let t2 = (i + 1) as f32 / n_strips as f32; + let c = lerp_color(hover_top, hover_bottom, (t + t2) * 0.5); + let y0 = rect.top() + rect.height() * t; + let y1 = rect.top() + rect.height() * t2; + let strip_rect = egui::Rect::from_min_max( + egui::pos2(rect.left(), y0), + egui::pos2(rect.right(), y1), + ); + ui.painter().rect(strip_rect, Rounding::ZERO, c, Stroke::NONE); + } + } + + ui.painter().rect(rect, rounding, Color32::TRANSPARENT, Stroke::new(1.0, border_color)); + + ui.painter().galley( + egui::pos2(rect.center().x - galley.size().x / 2.0, rect.center().y - galley.size().y / 2.0), + galley, + text_color, + ); + + if let Some(cursor) = ui.visuals().interact_cursor { + if response.hovered() { + ui.ctx().set_cursor_icon(cursor); + } + } + + response +} + +pub fn primary_button(ui: &mut egui::Ui, text: &str) -> egui::Response { + gradient_button(ui, text, + Color32::from_rgb(50, 130, 255), + Color32::from_rgb(13, 110, 253), + Color32::WHITE, + Color32::from_rgb(70, 150, 255), + Color32::from_rgb(30, 130, 255), + Color32::from_rgb(13, 110, 253)) +} + +pub fn secondary_button(ui: &mut egui::Ui, text: &str) -> egui::Response { + gradient_button(ui, text, + Color32::from_rgb(130, 140, 150), + Color32::from_rgb(100, 110, 120), + Color32::from_rgb(158, 158, 175), + Color32::from_rgb(150, 160, 170), + Color32::from_rgb(120, 130, 140), + Color32::from_rgb(100, 110, 120)) +} + +pub fn danger_button(ui: &mut egui::Ui, text: &str) -> egui::Response { + gradient_button(ui, text, + Color32::from_rgb(240, 83, 99), + Color32::from_rgb(200, 40, 60), + Color32::WHITE, + Color32::from_rgb(255, 100, 120), + Color32::from_rgb(220, 60, 80), + Color32::from_rgb(200, 40, 60)) +} + +#[allow(dead_code)] +pub fn white_grd_btn(ui: &mut egui::Ui, text: &str) -> egui::Response { + gradient_button(ui, text, + Color32::from_rgb(255, 255, 255), + Color32::from_rgb(246, 246, 246), + Color32::from_rgb(102, 102, 102), + Color32::from_rgb(246, 246, 246), + Color32::from_rgb(255, 255, 255), + Color32::from_rgb(177, 177, 177)) +} +#[allow(dead_code)] +pub fn btn_dark_blue(ui: &mut egui::Ui, text: &str) -> egui::Response { + gradient_button(ui, text, + Color32::from_rgb(99, 184, 238), + Color32::from_rgb(70, 140, 207), + Color32::WHITE, + Color32::from_rgb(70, 140, 207), + Color32::from_rgb(99, 184, 238), + Color32::from_rgb(56, 102, 163)) +} + +#[allow(dead_code)] +pub fn light_gray_btn(ui: &mut egui::Ui, text: &str) -> egui::Response { + gradient_button(ui, text, + Color32::from_rgb(249, 249, 249), // üst: #f9f9f9 + Color32::from_rgb(233, 233, 233), // alt: #e9e9e9 + Color32::from_rgb(102, 102, 102), // metin: #666666 + Color32::from_rgb(233, 233, 233), // hover üst (ters) + Color32::from_rgb(249, 249, 249), // hover alt (ters) + Color32::from_rgb(227, 195, 227)) // border: #e3c7e3 +} diff --git a/src/ui/mod.rs b/src/ui/mod.rs index 63895a4..c46888f 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -1,4 +1,5 @@ pub mod theme; +pub mod buttons; pub mod slideshow; pub mod error_screen; diff --git a/src/ui/theme.rs b/src/ui/theme.rs index 060d3af..00c7fde 100644 --- a/src/ui/theme.rs +++ b/src/ui/theme.rs @@ -15,6 +15,8 @@ use std::sync::atomic::{AtomicU32, Ordering}; static BG_DARK: AtomicU32 = AtomicU32::new(0x121214); static BG_PANEL: AtomicU32 = AtomicU32::new(0x1A1A1E); static BG_WIDGET: AtomicU32 = AtomicU32::new(0x242428); + + static BORDER: AtomicU32 = AtomicU32::new(0x323238); static TEXT: AtomicU32 = AtomicU32::new(0xF3F3F5); static TEXT_DIM: AtomicU32 = AtomicU32::new(0x9E9EAF); @@ -284,96 +286,14 @@ fn build_style() -> egui::Style { ] .into(); + style.visuals.interact_cursor = Some(egui::CursorIcon::PointingHand); + style } // ─── Yardımcı widget'lar ───────────────────────────────────────── -fn lerp_color(a: Color32, b: Color32, t: f32) -> Color32 { - Color32::from_rgba_premultiplied( - (a.r() as f32 + (b.r() as f32 - a.r() as f32) * t) as u8, - (a.g() as f32 + (b.g() as f32 - a.g() as f32) * t) as u8, - (a.b() as f32 + (b.b() as f32 - a.b() as f32) * t) as u8, - (a.a() as f32 + (b.a() as f32 - a.a() as f32) * t) as u8, - ) -} - -fn gradient_button(ui: &mut egui::Ui, text: &str, top_color: Color32, bottom_color: Color32, text_color: Color32) -> egui::Response { - let rounding = Rounding::same(6.0); - let min_size = Vec2::new(100.0, 32.0); - let padding = Vec2::new(12.0, 6.0); - - let font_id = ui.style().text_styles.get(&egui::TextStyle::Button) - .cloned().unwrap_or(FontId::proportional(14.0)); - let galley = ui.painter().layout_no_wrap( - text.to_string(), - font_id, - text_color, - ); - - let size = Vec2::new( - min_size.x.max(galley.size().x + 2.0 * padding.x), - min_size.y.max(galley.size().y + 2.0 * padding.y), - ); - - let (rect, response) = ui.allocate_exact_size(size, egui::Sense::click()); - - // Gradient arka plan (16 yatay şerit) - let n_strips = 16; - for i in 0..n_strips { - let t = i as f32 / n_strips as f32; - let t2 = (i + 1) as f32 / n_strips as f32; - let c = lerp_color(top_color, bottom_color, (t + t2) * 0.5); - let y0 = rect.top() + rect.height() * t; - let y1 = rect.top() + rect.height() * t2; - let strip_rect = egui::Rect::from_min_max( - egui::pos2(rect.left(), y0), - egui::pos2(rect.right(), y1), - ); - ui.painter().rect(strip_rect, Rounding::ZERO, c, Stroke::NONE); - } - - // Yuvarlak köşe efekti için border - ui.painter().rect(rect, rounding, Color32::TRANSPARENT, Stroke::new(1.0, bottom_color)); - - // Hover efekti - if response.hovered() { - ui.painter().rect(rect, rounding, Color32::WHITE.linear_multiply(0.10), Stroke::NONE); - } - - // Metin - ui.painter().galley( - egui::pos2(rect.center().x - galley.size().x / 2.0, rect.center().y - galley.size().y / 2.0), - galley, - text_color, - ); - - response -} - -/// Ana eylem butonu (mavi gradient). -pub fn primary_button(ui: &mut egui::Ui, text: &str) -> egui::Response { - gradient_button(ui, text, - Color32::from_rgb(50, 130, 255), // üst: açık mavi - Color32::from_rgb(13, 110, 253), // alt: koyu mavi - Color32::WHITE) -} - -/// İkincil buton (gri gradient). -pub fn secondary_button(ui: &mut egui::Ui, text: &str) -> egui::Response { - gradient_button(ui, text, - Color32::from_rgb(130, 140, 150), // üst: açık gri - Color32::from_rgb(100, 110, 120), // alt: koyu gri - c_text_dim()) -} - -/// İptal/Danger butonu (kırmızı gradient). -pub fn danger_button(ui: &mut egui::Ui, text: &str) -> egui::Response { - gradient_button(ui, text, - Color32::from_rgb(240, 83, 99), // üst: açık kırmızı - Color32::from_rgb(200, 40, 60), // alt: koyu kırmızı - Color32::WHITE) -} +pub use super::buttons::*; /// Bölüm başlığı — büyük, açık renkli, alt çizgisiz. pub fn section_heading(ui: &mut egui::Ui, text: &str) {