@@ -348,8 +348,8 @@ impl CalculatorUI { |
| 348 | 348 | |
| 349 | 349 | fn help_modal_rect(&self) -> Rect { |
| 350 | 350 | let size = self.renderer.size(); |
| 351 | | - let width = ((size.width as f64 * 0.72).clamp(460.0, 760.0)).round() as u32; |
| 352 | | - let height = ((size.height as f64 * 0.74).clamp(360.0, 560.0)).round() as u32; |
| 351 | + let width = ((size.width as f64 * 0.82).clamp(520.0, 920.0)).round() as u32; |
| 352 | + let height = ((size.height as f64 * 0.82).clamp(420.0, 700.0)).round() as u32; |
| 353 | 353 | Rect::new( |
| 354 | 354 | ((size.width - width) / 2) as i32, |
| 355 | 355 | ((size.height - height) / 2) as i32, |
@@ -430,83 +430,186 @@ impl CalculatorUI { |
| 430 | 430 | &title_style, |
| 431 | 431 | )?; |
| 432 | 432 | |
| 433 | + let sub_style = TextStyle::new() |
| 434 | + .font_family(&self.theme.font_family) |
| 435 | + .font_size(11.0) |
| 436 | + .color(self.theme.foreground.with_alpha(0.75)); |
| 437 | + self.renderer.text( |
| 438 | + "Shortcuts, structured math input, and mode controls", |
| 439 | + (modal.x + 20) as f64, |
| 440 | + (modal.y + 36) as f64, |
| 441 | + &sub_style, |
| 442 | + )?; |
| 443 | + |
| 433 | 444 | let heading_style = TextStyle::new() |
| 434 | 445 | .font_family(&self.theme.font_family) |
| 435 | 446 | .font_size(13.0) |
| 436 | 447 | .color(self.theme.selection_foreground); |
| 437 | 448 | let body_style = TextStyle::new() |
| 438 | 449 | .font_family(&self.theme.font_family) |
| 439 | | - .font_size(12.0) |
| 450 | + .font_size(11.5) |
| 440 | 451 | .color(self.theme.foreground.with_alpha(0.92)); |
| 441 | 452 | let hint_style = TextStyle::new() |
| 442 | 453 | .font_family(&self.theme.font_family) |
| 443 | 454 | .font_size(11.0) |
| 444 | 455 | .color(self.theme.foreground.with_alpha(0.75)); |
| 445 | 456 | |
| 446 | | - let mut y = modal.y + 48; |
| 447 | | - let x = modal.x + 20; |
| 448 | | - |
| 457 | + let content_top = modal.y + 56; |
| 458 | + let content_left = modal.x + 20; |
| 459 | + let content_width = modal.width as i32 - 40; |
| 460 | + let column_gap = 24; |
| 461 | + let column_width = (content_width - column_gap) / 2; |
| 462 | + let left_x = content_left; |
| 463 | + let right_x = content_left + column_width + column_gap; |
| 464 | + let divider_x = content_left + column_width + (column_gap / 2); |
| 465 | + let divider_rect = Rect::new( |
| 466 | + divider_x, |
| 467 | + content_top + 2, |
| 468 | + 1, |
| 469 | + modal.height.saturating_sub(94), |
| 470 | + ); |
| 449 | 471 | self.renderer |
| 450 | | - .text("Common expressions", x as f64, y as f64, &heading_style)?; |
| 451 | | - y += 22; |
| 452 | | - for line in [ |
| 453 | | - "2+2, sin(pi/2), sqrt(2), x^2 + 2*x + 1", |
| 454 | | - "diff(x^2, x), integrate(sin(x), x), solve(x^2-4, x)", |
| 455 | | - "sum(k, k, 1, n), product(k, k, 1, n)", |
| 456 | | - ] { |
| 457 | | - self.renderer |
| 458 | | - .text(&format!("- {line}"), x as f64, y as f64, &body_style)?; |
| 459 | | - y += 18; |
| 460 | | - } |
| 461 | | - |
| 462 | | - y += 10; |
| 463 | | - self.renderer.text( |
| 464 | | - "Structured input commands", |
| 465 | | - x as f64, |
| 466 | | - y as f64, |
| 472 | + .fill_rect(divider_rect, self.theme.border.with_alpha(0.45))?; |
| 473 | + |
| 474 | + let mut left_y = content_top; |
| 475 | + let mut right_y = content_top; |
| 476 | + |
| 477 | + self.draw_help_section( |
| 478 | + left_x, |
| 479 | + &mut left_y, |
| 480 | + "Quick Start", |
| 481 | + &[ |
| 482 | + "Enter evaluates the current expression.", |
| 483 | + "Esc clears input (or closes this help).", |
| 484 | + "Type '?' or click ? to toggle this panel.", |
| 485 | + "F1 calculator, F2 graph, F3 graph 3D.", |
| 486 | + ], |
| 467 | 487 | &heading_style, |
| 488 | + &body_style, |
| 489 | + )?; |
| 490 | + left_y += 6; |
| 491 | + |
| 492 | + self.draw_help_section( |
| 493 | + left_x, |
| 494 | + &mut left_y, |
| 495 | + "Structured Templates", |
| 496 | + &[ |
| 497 | + "Type '\\' or ':' then command + Space/Enter.", |
| 498 | + "\\frac \\sqrt \\nthroot \\abs \\matrix", |
| 499 | + "\\sum \\prod \\int \\dint \\lim \\diff", |
| 500 | + "\\solve \\sin \\cos \\tan \\ln \\exp", |
| 501 | + "Tab / Shift+Tab moves between template slots.", |
| 502 | + ], |
| 503 | + &heading_style, |
| 504 | + &body_style, |
| 505 | + )?; |
| 506 | + left_y += 6; |
| 507 | + |
| 508 | + self.draw_help_section( |
| 509 | + left_x, |
| 510 | + &mut left_y, |
| 511 | + "Editing And History", |
| 512 | + &[ |
| 513 | + "Arrow keys move inside boxes and fractions.", |
| 514 | + "Backspace/Delete remove chars or empty boxes.", |
| 515 | + "Home/End jumps to start/end of input.", |
| 516 | + "Use '^', '_', '/', '!' for power/sub/frac/factorial.", |
| 517 | + "Up/Down on blank input recalls history.", |
| 518 | + "Ctrl+Up/Down forces history browsing.", |
| 519 | + ], |
| 520 | + &heading_style, |
| 521 | + &body_style, |
| 468 | 522 | )?; |
| 469 | | - y += 22; |
| 470 | | - for line in [ |
| 471 | | - "Type '\\' then command and press Enter/Space.", |
| 472 | | - "\\frac \\sqrt \\sum \\prod \\diff \\dint \\lim \\solve", |
| 473 | | - ] { |
| 474 | | - self.renderer |
| 475 | | - .text(&format!("- {line}"), x as f64, y as f64, &body_style)?; |
| 476 | | - y += 18; |
| 477 | | - } |
| 478 | 523 | |
| 479 | | - y += 10; |
| 480 | | - self.renderer |
| 481 | | - .text("Keybinds", x as f64, y as f64, &heading_style)?; |
| 482 | | - y += 22; |
| 483 | | - for line in [ |
| 484 | | - "Enter evaluate, Tab/Shift+Tab move slot focus", |
| 485 | | - "Arrow keys move cursor, Up/Down recall history when input is blank", |
| 486 | | - "Ctrl+Up/Down force calculator history recall", |
| 487 | | - "F1 calculator, F2 graph, F3 3D, Esc closes this help", |
| 488 | | - ] { |
| 489 | | - self.renderer |
| 490 | | - .text(&format!("- {line}"), x as f64, y as f64, &body_style)?; |
| 491 | | - y += 18; |
| 492 | | - } |
| 524 | + self.draw_help_section( |
| 525 | + right_x, |
| 526 | + &mut right_y, |
| 527 | + "Calculator Examples", |
| 528 | + &[ |
| 529 | + "2+2", |
| 530 | + "diff(x^2, x)", |
| 531 | + "integrate(sin(x), x)", |
| 532 | + "sum(k^2+k, k, 1, n)", |
| 533 | + "solve(x^2-4, x)", |
| 534 | + ], |
| 535 | + &heading_style, |
| 536 | + &body_style, |
| 537 | + )?; |
| 538 | + right_y += 6; |
| 539 | + |
| 540 | + self.draw_help_section( |
| 541 | + right_x, |
| 542 | + &mut right_y, |
| 543 | + "Graph Mode", |
| 544 | + &[ |
| 545 | + "Enter y=... (or expression) to add a curve.", |
| 546 | + "Left-drag pans, mouse wheel zooms.", |
| 547 | + "Right-click toggles trace cursor.", |
| 548 | + "Ctrl+R reset view, Ctrl+C clear, Ctrl+T trace.", |
| 549 | + ], |
| 550 | + &heading_style, |
| 551 | + &body_style, |
| 552 | + )?; |
| 553 | + right_y += 6; |
| 554 | + |
| 555 | + self.draw_help_section( |
| 556 | + right_x, |
| 557 | + &mut right_y, |
| 558 | + "3D Mode", |
| 559 | + &[ |
| 560 | + "Enter z=... (or expression) to add a surface.", |
| 561 | + "Left-drag rotates camera, mouse wheel zooms.", |
| 562 | + "Ctrl+R reset camera, Ctrl+C clear surfaces.", |
| 563 | + "Parametric surface: (x(u,v), y(u,v), z(u,v)).", |
| 564 | + ], |
| 565 | + &heading_style, |
| 566 | + &body_style, |
| 567 | + )?; |
| 568 | + right_y += 6; |
| 569 | + |
| 570 | + self.draw_help_section( |
| 571 | + right_x, |
| 572 | + &mut right_y, |
| 573 | + "Behavior Notes", |
| 574 | + &[ |
| 575 | + "Pretty output renders many symbolic forms directly.", |
| 576 | + "Definite integrals with numeric bounds evaluate numerically.", |
| 577 | + "Hard symbolic forms may remain as integrate(...).", |
| 578 | + "Click outside modal (or x) to close.", |
| 579 | + ], |
| 580 | + &heading_style, |
| 581 | + &body_style, |
| 582 | + )?; |
| 493 | 583 | |
| 494 | | - y += 8; |
| 495 | 584 | let mode_hint = match mode { |
| 496 | | - Mode::Graph => "Graph mode: drag to pan, scroll to zoom, right-click trace toggle.", |
| 497 | | - Mode::Graph3D => "3D mode: drag to rotate camera, scroll to zoom depth.", |
| 498 | | - _ => "Calculator mode: use pretty templates, then evaluate with Enter.", |
| 585 | + Mode::Graph => "Active mode: Graph.", |
| 586 | + Mode::Graph3D => "Active mode: Graph 3D.", |
| 587 | + _ => "Active mode: Calculator.", |
| 499 | 588 | }; |
| 589 | + let hint_y = modal.bottom() - 24; |
| 500 | 590 | self.renderer |
| 501 | | - .text(mode_hint, x as f64, y as f64, &hint_style)?; |
| 502 | | - y += 20; |
| 503 | | - self.renderer.text( |
| 504 | | - "Tip: click outside this panel (or x) to close.", |
| 505 | | - x as f64, |
| 506 | | - y as f64, |
| 507 | | - &hint_style, |
| 508 | | - )?; |
| 591 | + .text(mode_hint, (modal.x + 20) as f64, hint_y as f64, &hint_style)?; |
| 592 | + |
| 593 | + Ok(()) |
| 594 | + } |
| 509 | 595 | |
| 596 | + fn draw_help_section( |
| 597 | + &mut self, |
| 598 | + x: i32, |
| 599 | + y: &mut i32, |
| 600 | + heading: &str, |
| 601 | + lines: &[&str], |
| 602 | + heading_style: &TextStyle, |
| 603 | + body_style: &TextStyle, |
| 604 | + ) -> Result<()> { |
| 605 | + self.renderer |
| 606 | + .text(heading, x as f64, *y as f64, heading_style)?; |
| 607 | + *y += 20; |
| 608 | + for line in lines { |
| 609 | + self.renderer |
| 610 | + .text(&format!("- {line}"), x as f64, *y as f64, body_style)?; |
| 611 | + *y += 16; |
| 612 | + } |
| 510 | 613 | Ok(()) |
| 511 | 614 | } |
| 512 | 615 | |