From 2550e13b1058801d34a533b32ba081c54e272a87 Mon Sep 17 00:00:00 2001 From: minsung Date: Tue, 14 Apr 2026 20:50:37 +0900 Subject: [PATCH] =?UTF-8?q?viewer=20Sprint=205=20=EC=99=84=EC=84=B1=20?= =?UTF-8?q?=E2=80=94=20=ED=95=9C=EA=B8=80=20=ED=8F=B0=ED=8A=B8=20+=20?= =?UTF-8?q?=EC=8A=AC=EB=9D=BC=EC=9D=B4=EB=8D=94=20=EC=9E=90=EB=8F=99=20?= =?UTF-8?q?=EC=9E=AC=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Windows Malgun Gothic 폰트 로드 (한글 지원) - param_slider! 매크로: drag_released() / lost_focus() 시 자동 rebuild_mesh() - "▶ 적용 (Apply)" 버튼은 manual fallback으로 유지 Co-Authored-By: Claude Opus 4.6 (1M context) --- cimery/crates/viewer/src/lib.rs | 66 ++++++++++++++++++++------------- 1 file changed, 40 insertions(+), 26 deletions(-) diff --git a/cimery/crates/viewer/src/lib.rs b/cimery/crates/viewer/src/lib.rs index 117229a..f53df9b 100644 --- a/cimery/crates/viewer/src/lib.rs +++ b/cimery/crates/viewer/src/lib.rs @@ -278,6 +278,27 @@ impl RenderState { // ── egui ────────────────────────────────────────────────────────────── let egui_ctx = egui::Context::default(); + + // Load Korean system font (Windows Malgun Gothic) for CJK support + { + let mut fonts = egui::FontDefinitions::default(); + let font_path = "C:\\Windows\\Fonts\\malgun.ttf"; + if let Ok(data) = std::fs::read(font_path) { + fonts.font_data.insert( + "MalgunGothic".to_owned(), + egui::FontData::from_owned(data), + ); + // Insert as primary font (index 0) so Korean renders by default + for family in fonts.families.values_mut() { + family.insert(0, "MalgunGothic".to_owned()); + } + egui_ctx.set_fonts(fonts); + log::info!("Korean font loaded: {}", font_path); + } else { + log::warn!("Korean font not found at {}; UI labels will show boxes", font_path); + } + } + let egui_state = egui_winit::State::new( egui_ctx.clone(), egui::ViewportId::ROOT, @@ -408,35 +429,28 @@ impl RenderState { ui.heading("교량 속성"); ui.separator(); - let prev = p.span_m; - ui.label("경간 (m)"); - ui.add(egui::Slider::new(&mut p.span_m, 20.0..=80.0).step_by(1.0)); - if (p.span_m - prev).abs() > 0.001 { dirty = true; } + // Helper: slider that auto-applies on drag release + macro_rules! param_slider { + ($label:expr, $val:expr, $range:expr, $step:expr) => {{ + ui.label($label); + let r = ui.add(egui::Slider::new($val, $range).step_by($step)); + if r.drag_released() || r.lost_focus() { apply = true; dirty = true; } + if r.changed() { dirty = true; } + }}; + } - let prev = p.girder_count; - ui.label("거더 수"); - ui.add(egui::Slider::new(&mut p.girder_count, 3..=7)); - if p.girder_count != prev { dirty = true; } - - let prev = p.girder_spacing; - ui.label("c/c 간격 (mm)"); - ui.add(egui::Slider::new(&mut p.girder_spacing, 1_500.0..=4_000.0).step_by(100.0)); - if (p.girder_spacing - prev).abs() > 1.0 { dirty = true; } - - let prev = p.girder_height; - ui.label("거더 높이 (mm)"); - ui.add(egui::Slider::new(&mut p.girder_height, 1_000.0..=3_000.0).step_by(100.0)); - if (p.girder_height - prev).abs() > 1.0 { dirty = true; } - - let prev = p.slab_thickness; - ui.label("슬래브 두께 (mm)"); - ui.add(egui::Slider::new(&mut p.slab_thickness, 150.0..=400.0).step_by(10.0)); - if (p.slab_thickness - prev).abs() > 1.0 { dirty = true; } + param_slider!("경간 (m)", &mut p.span_m, 20.0..=80.0, 1.0); + param_slider!("거더 수", &mut p.girder_count, 3..=7, 1.0); + param_slider!("c/c 간격 (mm)", &mut p.girder_spacing, 1_500.0..=4_000.0, 100.0); + param_slider!("거더 높이 (mm)",&mut p.girder_height, 1_000.0..=3_000.0, 100.0); + param_slider!("슬래브 두께(mm)",&mut p.slab_thickness, 150.0..=400.0, 10.0); ui.separator(); - if dirty { - apply = ui.button("▶ 적용").clicked(); - } else { + if dirty && !apply { + // Manual apply button as fallback + if ui.button("▶ 적용 (Apply)").clicked() { apply = true; } + ui.small("* 슬라이더를 놓으면 자동 적용"); + } else if !dirty { ui.label("✓ 최신 상태"); }