claude-mpm 4.15.6__py3-none-any.whl → 4.21.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of claude-mpm might be problematic. Click here for more details.
- claude_mpm/VERSION +1 -1
- claude_mpm/agents/BASE_ENGINEER.md +286 -0
- claude_mpm/agents/BASE_PM.md +272 -23
- claude_mpm/agents/PM_INSTRUCTIONS.md +49 -0
- claude_mpm/agents/agent_loader.py +4 -4
- claude_mpm/agents/templates/engineer.json +5 -1
- claude_mpm/agents/templates/php-engineer.json +10 -4
- claude_mpm/agents/templates/python_engineer.json +8 -3
- claude_mpm/agents/templates/rust_engineer.json +12 -7
- claude_mpm/agents/templates/svelte-engineer.json +225 -0
- claude_mpm/cli/commands/__init__.py +2 -0
- claude_mpm/cli/commands/mpm_init/__init__.py +73 -0
- claude_mpm/cli/commands/mpm_init/core.py +525 -0
- claude_mpm/cli/commands/mpm_init/display.py +341 -0
- claude_mpm/cli/commands/mpm_init/git_activity.py +427 -0
- claude_mpm/cli/commands/mpm_init/modes.py +397 -0
- claude_mpm/cli/commands/mpm_init/prompts.py +442 -0
- claude_mpm/cli/commands/mpm_init_cli.py +396 -0
- claude_mpm/cli/commands/mpm_init_handler.py +67 -1
- claude_mpm/cli/commands/skills.py +488 -0
- claude_mpm/cli/executor.py +2 -0
- claude_mpm/cli/parsers/base_parser.py +7 -0
- claude_mpm/cli/parsers/mpm_init_parser.py +42 -0
- claude_mpm/cli/parsers/skills_parser.py +137 -0
- claude_mpm/cli/startup.py +57 -0
- claude_mpm/commands/mpm-auto-configure.md +52 -0
- claude_mpm/commands/mpm-help.md +6 -0
- claude_mpm/commands/mpm-init.md +112 -6
- claude_mpm/commands/mpm-resume.md +372 -0
- claude_mpm/commands/mpm-version.md +113 -0
- claude_mpm/commands/mpm.md +2 -0
- claude_mpm/config/agent_config.py +2 -2
- claude_mpm/constants.py +12 -0
- claude_mpm/core/config.py +42 -0
- claude_mpm/core/factories.py +1 -1
- claude_mpm/core/interfaces.py +56 -1
- claude_mpm/core/optimized_agent_loader.py +3 -3
- claude_mpm/hooks/__init__.py +8 -0
- claude_mpm/hooks/claude_hooks/response_tracking.py +35 -1
- claude_mpm/hooks/session_resume_hook.py +121 -0
- claude_mpm/models/resume_log.py +340 -0
- claude_mpm/services/agents/auto_config_manager.py +1 -1
- claude_mpm/services/agents/deployment/agent_configuration_manager.py +1 -1
- claude_mpm/services/agents/deployment/agent_record_service.py +1 -1
- claude_mpm/services/agents/deployment/agent_validator.py +17 -1
- claude_mpm/services/agents/deployment/async_agent_deployment.py +1 -1
- claude_mpm/services/agents/deployment/local_template_deployment.py +1 -1
- claude_mpm/services/agents/local_template_manager.py +1 -1
- claude_mpm/services/agents/recommender.py +47 -0
- claude_mpm/services/cli/resume_service.py +617 -0
- claude_mpm/services/cli/session_manager.py +87 -0
- claude_mpm/services/cli/session_pause_manager.py +504 -0
- claude_mpm/services/cli/session_resume_helper.py +372 -0
- claude_mpm/services/core/base.py +26 -11
- claude_mpm/services/core/interfaces.py +56 -1
- claude_mpm/services/core/models/agent_config.py +3 -0
- claude_mpm/services/core/models/process.py +4 -0
- claude_mpm/services/core/path_resolver.py +1 -1
- claude_mpm/services/diagnostics/models.py +21 -0
- claude_mpm/services/event_bus/relay.py +23 -7
- claude_mpm/services/infrastructure/resume_log_generator.py +439 -0
- claude_mpm/services/local_ops/__init__.py +2 -0
- claude_mpm/services/mcp_config_manager.py +7 -131
- claude_mpm/services/mcp_gateway/auto_configure.py +31 -25
- claude_mpm/services/mcp_gateway/core/process_pool.py +19 -10
- claude_mpm/services/mcp_gateway/tools/external_mcp_services.py +26 -21
- claude_mpm/services/memory/failure_tracker.py +19 -4
- claude_mpm/services/session_manager.py +205 -1
- claude_mpm/services/unified/deployment_strategies/local.py +1 -1
- claude_mpm/services/version_service.py +104 -1
- claude_mpm/skills/__init__.py +21 -0
- claude_mpm/skills/agent_skills_injector.py +324 -0
- claude_mpm/skills/bundled/LICENSE_ATTRIBUTIONS.md +79 -0
- claude_mpm/skills/bundled/api-documentation.md +393 -0
- claude_mpm/skills/bundled/async-testing.md +571 -0
- claude_mpm/skills/bundled/code-review.md +143 -0
- claude_mpm/skills/bundled/collaboration/brainstorming/SKILL.md +79 -0
- claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/SKILL.md +178 -0
- claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/references/agent-prompts.md +577 -0
- claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/references/coordination-patterns.md +467 -0
- claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/references/examples.md +537 -0
- claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/references/troubleshooting.md +730 -0
- claude_mpm/skills/bundled/collaboration/requesting-code-review/SKILL.md +112 -0
- claude_mpm/skills/bundled/collaboration/requesting-code-review/references/code-reviewer-template.md +146 -0
- claude_mpm/skills/bundled/collaboration/requesting-code-review/references/review-examples.md +412 -0
- claude_mpm/skills/bundled/collaboration/writing-plans/SKILL.md +81 -0
- claude_mpm/skills/bundled/collaboration/writing-plans/references/best-practices.md +362 -0
- claude_mpm/skills/bundled/collaboration/writing-plans/references/plan-structure-templates.md +312 -0
- claude_mpm/skills/bundled/database-migration.md +199 -0
- claude_mpm/skills/bundled/debugging/root-cause-tracing/SKILL.md +152 -0
- claude_mpm/skills/bundled/debugging/root-cause-tracing/references/advanced-techniques.md +668 -0
- claude_mpm/skills/bundled/debugging/root-cause-tracing/references/examples.md +587 -0
- claude_mpm/skills/bundled/debugging/root-cause-tracing/references/integration.md +438 -0
- claude_mpm/skills/bundled/debugging/root-cause-tracing/references/tracing-techniques.md +391 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/CREATION-LOG.md +119 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/SKILL.md +148 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/references/anti-patterns.md +483 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/references/examples.md +452 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/references/troubleshooting.md +449 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/references/workflow.md +411 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/test-academic.md +14 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/test-pressure-1.md +58 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/test-pressure-2.md +68 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/test-pressure-3.md +69 -0
- claude_mpm/skills/bundled/debugging/verification-before-completion/SKILL.md +131 -0
- claude_mpm/skills/bundled/debugging/verification-before-completion/references/gate-function.md +325 -0
- claude_mpm/skills/bundled/debugging/verification-before-completion/references/integration-and-workflows.md +490 -0
- claude_mpm/skills/bundled/debugging/verification-before-completion/references/red-flags-and-failures.md +425 -0
- claude_mpm/skills/bundled/debugging/verification-before-completion/references/verification-patterns.md +499 -0
- claude_mpm/skills/bundled/docker-containerization.md +194 -0
- claude_mpm/skills/bundled/express-local-dev.md +1429 -0
- claude_mpm/skills/bundled/fastapi-local-dev.md +1199 -0
- claude_mpm/skills/bundled/git-workflow.md +414 -0
- claude_mpm/skills/bundled/imagemagick.md +204 -0
- claude_mpm/skills/bundled/json-data-handling.md +223 -0
- claude_mpm/skills/bundled/main/artifacts-builder/SKILL.md +86 -0
- claude_mpm/skills/bundled/main/internal-comms/SKILL.md +43 -0
- claude_mpm/skills/bundled/main/internal-comms/examples/3p-updates.md +47 -0
- claude_mpm/skills/bundled/main/internal-comms/examples/company-newsletter.md +65 -0
- claude_mpm/skills/bundled/main/internal-comms/examples/faq-answers.md +30 -0
- claude_mpm/skills/bundled/main/internal-comms/examples/general-comms.md +16 -0
- claude_mpm/skills/bundled/main/mcp-builder/SKILL.md +160 -0
- claude_mpm/skills/bundled/main/mcp-builder/reference/design_principles.md +412 -0
- claude_mpm/skills/bundled/main/mcp-builder/reference/evaluation.md +602 -0
- claude_mpm/skills/bundled/main/mcp-builder/reference/mcp_best_practices.md +915 -0
- claude_mpm/skills/bundled/main/mcp-builder/reference/node_mcp_server.md +916 -0
- claude_mpm/skills/bundled/main/mcp-builder/reference/python_mcp_server.md +752 -0
- claude_mpm/skills/bundled/main/mcp-builder/reference/workflow.md +1237 -0
- claude_mpm/skills/bundled/main/mcp-builder/scripts/connections.py +157 -0
- claude_mpm/skills/bundled/main/mcp-builder/scripts/evaluation.py +425 -0
- claude_mpm/skills/bundled/main/skill-creator/SKILL.md +189 -0
- claude_mpm/skills/bundled/main/skill-creator/references/best-practices.md +500 -0
- claude_mpm/skills/bundled/main/skill-creator/references/creation-workflow.md +464 -0
- claude_mpm/skills/bundled/main/skill-creator/references/examples.md +619 -0
- claude_mpm/skills/bundled/main/skill-creator/references/progressive-disclosure.md +437 -0
- claude_mpm/skills/bundled/main/skill-creator/references/skill-structure.md +231 -0
- claude_mpm/skills/bundled/main/skill-creator/scripts/init_skill.py +303 -0
- claude_mpm/skills/bundled/main/skill-creator/scripts/package_skill.py +113 -0
- claude_mpm/skills/bundled/main/skill-creator/scripts/quick_validate.py +72 -0
- claude_mpm/skills/bundled/nextjs-local-dev.md +807 -0
- claude_mpm/skills/bundled/pdf.md +141 -0
- claude_mpm/skills/bundled/performance-profiling.md +567 -0
- claude_mpm/skills/bundled/php/espocrm-development/SKILL.md +170 -0
- claude_mpm/skills/bundled/php/espocrm-development/references/architecture.md +602 -0
- claude_mpm/skills/bundled/php/espocrm-development/references/common-tasks.md +821 -0
- claude_mpm/skills/bundled/php/espocrm-development/references/development-workflow.md +742 -0
- claude_mpm/skills/bundled/php/espocrm-development/references/frontend-customization.md +726 -0
- claude_mpm/skills/bundled/php/espocrm-development/references/hooks-and-services.md +764 -0
- claude_mpm/skills/bundled/php/espocrm-development/references/testing-debugging.md +831 -0
- claude_mpm/skills/bundled/refactoring-patterns.md +180 -0
- claude_mpm/skills/bundled/rust/desktop-applications/SKILL.md +226 -0
- claude_mpm/skills/bundled/rust/desktop-applications/references/architecture-patterns.md +901 -0
- claude_mpm/skills/bundled/rust/desktop-applications/references/native-gui-frameworks.md +901 -0
- claude_mpm/skills/bundled/rust/desktop-applications/references/platform-integration.md +775 -0
- claude_mpm/skills/bundled/rust/desktop-applications/references/state-management.md +937 -0
- claude_mpm/skills/bundled/rust/desktop-applications/references/tauri-framework.md +770 -0
- claude_mpm/skills/bundled/rust/desktop-applications/references/testing-deployment.md +961 -0
- claude_mpm/skills/bundled/security-scanning.md +327 -0
- claude_mpm/skills/bundled/systematic-debugging.md +473 -0
- claude_mpm/skills/bundled/test-driven-development.md +378 -0
- claude_mpm/skills/bundled/testing/condition-based-waiting/SKILL.md +119 -0
- claude_mpm/skills/bundled/testing/condition-based-waiting/references/patterns-and-implementation.md +253 -0
- claude_mpm/skills/bundled/testing/test-driven-development/SKILL.md +145 -0
- claude_mpm/skills/bundled/testing/test-driven-development/references/anti-patterns.md +543 -0
- claude_mpm/skills/bundled/testing/test-driven-development/references/examples.md +741 -0
- claude_mpm/skills/bundled/testing/test-driven-development/references/integration.md +470 -0
- claude_mpm/skills/bundled/testing/test-driven-development/references/philosophy.md +458 -0
- claude_mpm/skills/bundled/testing/test-driven-development/references/workflow.md +639 -0
- claude_mpm/skills/bundled/testing/testing-anti-patterns/SKILL.md +140 -0
- claude_mpm/skills/bundled/testing/testing-anti-patterns/references/completeness-anti-patterns.md +572 -0
- claude_mpm/skills/bundled/testing/testing-anti-patterns/references/core-anti-patterns.md +411 -0
- claude_mpm/skills/bundled/testing/testing-anti-patterns/references/detection-guide.md +569 -0
- claude_mpm/skills/bundled/testing/testing-anti-patterns/references/tdd-connection.md +695 -0
- claude_mpm/skills/bundled/testing/webapp-testing/SKILL.md +184 -0
- claude_mpm/skills/bundled/testing/webapp-testing/decision-tree.md +459 -0
- claude_mpm/skills/bundled/testing/webapp-testing/examples/console_logging.py +35 -0
- claude_mpm/skills/bundled/testing/webapp-testing/examples/element_discovery.py +44 -0
- claude_mpm/skills/bundled/testing/webapp-testing/examples/static_html_automation.py +34 -0
- claude_mpm/skills/bundled/testing/webapp-testing/playwright-patterns.md +479 -0
- claude_mpm/skills/bundled/testing/webapp-testing/reconnaissance-pattern.md +687 -0
- claude_mpm/skills/bundled/testing/webapp-testing/scripts/with_server.py +129 -0
- claude_mpm/skills/bundled/testing/webapp-testing/server-management.md +758 -0
- claude_mpm/skills/bundled/testing/webapp-testing/troubleshooting.md +868 -0
- claude_mpm/skills/bundled/vite-local-dev.md +1061 -0
- claude_mpm/skills/bundled/web-performance-optimization.md +2305 -0
- claude_mpm/skills/bundled/xlsx.md +157 -0
- claude_mpm/skills/registry.py +97 -9
- claude_mpm/skills/skills_registry.py +348 -0
- claude_mpm/skills/skills_service.py +739 -0
- claude_mpm/tools/code_tree_analyzer/__init__.py +45 -0
- claude_mpm/tools/code_tree_analyzer/analysis.py +299 -0
- claude_mpm/tools/code_tree_analyzer/cache.py +131 -0
- claude_mpm/tools/code_tree_analyzer/core.py +380 -0
- claude_mpm/tools/code_tree_analyzer/discovery.py +403 -0
- claude_mpm/tools/code_tree_analyzer/events.py +168 -0
- claude_mpm/tools/code_tree_analyzer/gitignore.py +308 -0
- claude_mpm/tools/code_tree_analyzer/models.py +39 -0
- claude_mpm/tools/code_tree_analyzer/multilang_analyzer.py +224 -0
- claude_mpm/tools/code_tree_analyzer/python_analyzer.py +284 -0
- claude_mpm/utils/agent_dependency_loader.py +2 -2
- {claude_mpm-4.15.6.dist-info → claude_mpm-4.21.3.dist-info}/METADATA +211 -33
- {claude_mpm-4.15.6.dist-info → claude_mpm-4.21.3.dist-info}/RECORD +206 -64
- claude_mpm/agents/INSTRUCTIONS_OLD_DEPRECATED.md +0 -602
- claude_mpm/cli/commands/mpm_init.py +0 -2008
- claude_mpm/tools/code_tree_analyzer.py +0 -1825
- {claude_mpm-4.15.6.dist-info → claude_mpm-4.21.3.dist-info}/WHEEL +0 -0
- {claude_mpm-4.15.6.dist-info → claude_mpm-4.21.3.dist-info}/entry_points.txt +0 -0
- {claude_mpm-4.15.6.dist-info → claude_mpm-4.21.3.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-4.15.6.dist-info → claude_mpm-4.21.3.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,901 @@
|
|
|
1
|
+
# Native GUI Frameworks
|
|
2
|
+
|
|
3
|
+
Comprehensive guide to pure Rust GUI frameworks: egui, iced, slint, and druid. When you need maximum performance, no web dependencies, or specialized UI patterns.
|
|
4
|
+
|
|
5
|
+
## Framework Overview
|
|
6
|
+
|
|
7
|
+
### Comparison Matrix
|
|
8
|
+
|
|
9
|
+
| Framework | Paradigm | Rendering | Best For | Maturity | Bundle Size |
|
|
10
|
+
|-----------|----------|-----------|----------|----------|-------------|
|
|
11
|
+
| **egui** | Immediate Mode | CPU (optional GPU) | Tools, editors, games | Mature | ~3MB |
|
|
12
|
+
| **iced** | Elm Architecture | GPU (wgpu) | Cross-platform apps | Growing | ~5MB |
|
|
13
|
+
| **slint** | Declarative | GPU/CPU | Embedded, desktop | Mature | ~4MB |
|
|
14
|
+
| **druid** | Data-first | GPU (piet) | Reactive apps | Maintenance | ~4MB |
|
|
15
|
+
|
|
16
|
+
### When to Use Each
|
|
17
|
+
|
|
18
|
+
**egui (Immediate Mode)**
|
|
19
|
+
- ✅ Game editors, debug tools, developer tools
|
|
20
|
+
- ✅ Rapid prototyping, quick iterations
|
|
21
|
+
- ✅ Dynamic UIs that change frequently
|
|
22
|
+
- ✅ Integration with game engines (Bevy, macroquad)
|
|
23
|
+
- ❌ Complex state management
|
|
24
|
+
- ❌ Strict design systems
|
|
25
|
+
|
|
26
|
+
**iced (Elm Architecture)**
|
|
27
|
+
- ✅ Cross-platform consistency
|
|
28
|
+
- ✅ Type-safe state management
|
|
29
|
+
- ✅ Predictable updates
|
|
30
|
+
- ✅ Custom widgets
|
|
31
|
+
- ❌ Steep learning curve
|
|
32
|
+
- ❌ Limited ecosystem
|
|
33
|
+
|
|
34
|
+
**slint (Declarative UI)**
|
|
35
|
+
- ✅ Embedded systems, IoT devices
|
|
36
|
+
- ✅ Designer-developer collaboration
|
|
37
|
+
- ✅ Declarative markup language
|
|
38
|
+
- ✅ Touch-first interfaces
|
|
39
|
+
- ❌ Larger binary size
|
|
40
|
+
- ❌ Proprietary markup
|
|
41
|
+
|
|
42
|
+
**druid (Data-first)**
|
|
43
|
+
- ✅ Data-driven applications
|
|
44
|
+
- ✅ Lens-based state management
|
|
45
|
+
- ✅ Widget composition
|
|
46
|
+
- ❌ Maintenance mode (archived)
|
|
47
|
+
- ❌ Limited documentation
|
|
48
|
+
|
|
49
|
+
## egui - Immediate Mode GUI
|
|
50
|
+
|
|
51
|
+
### Architecture
|
|
52
|
+
|
|
53
|
+
Immediate mode means UI is rebuilt every frame based on current state. No separate UI tree or state synchronization.
|
|
54
|
+
|
|
55
|
+
**Core Concepts:**
|
|
56
|
+
```rust
|
|
57
|
+
// Every frame:
|
|
58
|
+
1. Read input events
|
|
59
|
+
2. Run application logic
|
|
60
|
+
3. Build UI from scratch
|
|
61
|
+
4. Render output
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Setup
|
|
65
|
+
|
|
66
|
+
```toml
|
|
67
|
+
# Cargo.toml
|
|
68
|
+
[dependencies]
|
|
69
|
+
eframe = "0.27" # egui + native windowing
|
|
70
|
+
egui = "0.27"
|
|
71
|
+
|
|
72
|
+
# Optional: Additional widgets
|
|
73
|
+
egui_extras = "0.27"
|
|
74
|
+
egui_plot = "0.27"
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Basic Application
|
|
78
|
+
|
|
79
|
+
```rust
|
|
80
|
+
use eframe::egui;
|
|
81
|
+
|
|
82
|
+
fn main() -> Result<(), eframe::Error> {
|
|
83
|
+
let options = eframe::NativeOptions {
|
|
84
|
+
viewport: egui::ViewportBuilder::default()
|
|
85
|
+
.with_inner_size([800.0, 600.0]),
|
|
86
|
+
..Default::default()
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
eframe::run_native(
|
|
90
|
+
"My egui App",
|
|
91
|
+
options,
|
|
92
|
+
Box::new(|_cc| Box::new(MyApp::default())),
|
|
93
|
+
)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
struct MyApp {
|
|
97
|
+
name: String,
|
|
98
|
+
age: u32,
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
impl Default for MyApp {
|
|
102
|
+
fn default() -> Self {
|
|
103
|
+
Self {
|
|
104
|
+
name: "Arthur".to_owned(),
|
|
105
|
+
age: 42,
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
impl eframe::App for MyApp {
|
|
111
|
+
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
|
|
112
|
+
egui::CentralPanel::default().show(ctx, |ui| {
|
|
113
|
+
ui.heading("My egui Application");
|
|
114
|
+
|
|
115
|
+
ui.horizontal(|ui| {
|
|
116
|
+
ui.label("Name:");
|
|
117
|
+
ui.text_edit_singleline(&mut self.name);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
ui.add(egui::Slider::new(&mut self.age, 0..=120).text("age"));
|
|
121
|
+
|
|
122
|
+
if ui.button("Click me!").clicked() {
|
|
123
|
+
println!("Hello, {}! You are {} years old.", self.name, self.age);
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### Layout Patterns
|
|
131
|
+
|
|
132
|
+
```rust
|
|
133
|
+
impl eframe::App for MyApp {
|
|
134
|
+
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
|
|
135
|
+
// Top panel
|
|
136
|
+
egui::TopBottomPanel::top("top_panel").show(ctx, |ui| {
|
|
137
|
+
egui::menu::bar(ui, |ui| {
|
|
138
|
+
ui.menu_button("File", |ui| {
|
|
139
|
+
if ui.button("Open").clicked() {
|
|
140
|
+
// Handle open
|
|
141
|
+
}
|
|
142
|
+
if ui.button("Save").clicked() {
|
|
143
|
+
// Handle save
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
// Side panel
|
|
150
|
+
egui::SidePanel::left("side_panel").show(ctx, |ui| {
|
|
151
|
+
ui.heading("Settings");
|
|
152
|
+
ui.separator();
|
|
153
|
+
// Settings content
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
// Bottom panel
|
|
157
|
+
egui::TopBottomPanel::bottom("bottom_panel").show(ctx, |ui| {
|
|
158
|
+
ui.horizontal(|ui| {
|
|
159
|
+
ui.label("Status: Ready");
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
// Central panel (fills remaining space)
|
|
164
|
+
egui::CentralPanel::default().show(ctx, |ui| {
|
|
165
|
+
ui.heading("Main Content");
|
|
166
|
+
// Main application content
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### Widgets
|
|
173
|
+
|
|
174
|
+
```rust
|
|
175
|
+
// Text input
|
|
176
|
+
ui.text_edit_singleline(&mut self.text);
|
|
177
|
+
ui.text_edit_multiline(&mut self.multiline_text);
|
|
178
|
+
|
|
179
|
+
// Buttons
|
|
180
|
+
if ui.button("Click me").clicked() { }
|
|
181
|
+
if ui.small_button("Small").clicked() { }
|
|
182
|
+
ui.add_enabled(false, egui::Button::new("Disabled"));
|
|
183
|
+
|
|
184
|
+
// Checkboxes and radio
|
|
185
|
+
ui.checkbox(&mut self.checked, "Check me");
|
|
186
|
+
ui.radio_value(&mut self.choice, Choice::A, "Option A");
|
|
187
|
+
ui.radio_value(&mut self.choice, Choice::B, "Option B");
|
|
188
|
+
|
|
189
|
+
// Sliders and drag values
|
|
190
|
+
ui.add(egui::Slider::new(&mut self.value, 0.0..=100.0));
|
|
191
|
+
ui.add(egui::DragValue::new(&mut self.value).speed(0.1));
|
|
192
|
+
|
|
193
|
+
// Combo box (dropdown)
|
|
194
|
+
egui::ComboBox::from_label("Select item")
|
|
195
|
+
.selected_text(format!("{:?}", self.selected))
|
|
196
|
+
.show_ui(ui, |ui| {
|
|
197
|
+
ui.selectable_value(&mut self.selected, Item::A, "Item A");
|
|
198
|
+
ui.selectable_value(&mut self.selected, Item::B, "Item B");
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
// Color picker
|
|
202
|
+
ui.color_edit_button_rgb(&mut self.color);
|
|
203
|
+
|
|
204
|
+
// Images
|
|
205
|
+
ui.image(egui::include_image!("icon.png"));
|
|
206
|
+
|
|
207
|
+
// Plotting
|
|
208
|
+
use egui_plot::{Line, Plot, PlotPoints};
|
|
209
|
+
let sin: PlotPoints = (0..1000)
|
|
210
|
+
.map(|i| {
|
|
211
|
+
let x = i as f64 * 0.01;
|
|
212
|
+
[x, x.sin()]
|
|
213
|
+
})
|
|
214
|
+
.collect();
|
|
215
|
+
Plot::new("my_plot").show(ui, |plot_ui| {
|
|
216
|
+
plot_ui.line(Line::new(sin));
|
|
217
|
+
});
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
### Custom Widgets
|
|
221
|
+
|
|
222
|
+
```rust
|
|
223
|
+
fn custom_widget(ui: &mut egui::Ui, value: &mut f32) -> egui::Response {
|
|
224
|
+
let desired_size = ui.spacing().interact_size.y * egui::vec2(2.0, 1.0);
|
|
225
|
+
let (rect, response) = ui.allocate_exact_size(desired_size, egui::Sense::click());
|
|
226
|
+
|
|
227
|
+
if ui.is_rect_visible(rect) {
|
|
228
|
+
let visuals = ui.style().interact(&response);
|
|
229
|
+
let rect = rect.expand(visuals.expansion);
|
|
230
|
+
let radius = 0.5 * rect.height();
|
|
231
|
+
|
|
232
|
+
ui.painter()
|
|
233
|
+
.rect(rect, radius, visuals.bg_fill, visuals.bg_stroke);
|
|
234
|
+
|
|
235
|
+
// Draw custom content
|
|
236
|
+
let text = format!("{:.1}", value);
|
|
237
|
+
ui.painter().text(
|
|
238
|
+
rect.center(),
|
|
239
|
+
egui::Align2::CENTER_CENTER,
|
|
240
|
+
text,
|
|
241
|
+
egui::FontId::default(),
|
|
242
|
+
visuals.text_color(),
|
|
243
|
+
);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
response
|
|
247
|
+
}
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
### State Management with egui
|
|
251
|
+
|
|
252
|
+
```rust
|
|
253
|
+
use std::sync::{Arc, Mutex};
|
|
254
|
+
|
|
255
|
+
struct SharedState {
|
|
256
|
+
data: Arc<Mutex<AppData>>,
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
struct AppData {
|
|
260
|
+
counter: i32,
|
|
261
|
+
items: Vec<String>,
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
impl eframe::App for MyApp {
|
|
265
|
+
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
|
|
266
|
+
let mut data = self.state.data.lock().unwrap();
|
|
267
|
+
|
|
268
|
+
egui::CentralPanel::default().show(ctx, |ui| {
|
|
269
|
+
ui.label(format!("Counter: {}", data.counter));
|
|
270
|
+
|
|
271
|
+
if ui.button("Increment").clicked() {
|
|
272
|
+
data.counter += 1;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
for item in &data.items {
|
|
276
|
+
ui.label(item);
|
|
277
|
+
}
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
## iced - Elm Architecture
|
|
284
|
+
|
|
285
|
+
### Architecture
|
|
286
|
+
|
|
287
|
+
Iced follows The Elm Architecture: Model (state) + Update (state changes) + View (UI).
|
|
288
|
+
|
|
289
|
+
```
|
|
290
|
+
┌─────────────┐
|
|
291
|
+
│ View │ ─── Displays state
|
|
292
|
+
└──────┬──────┘
|
|
293
|
+
│ User interaction
|
|
294
|
+
▼
|
|
295
|
+
┌─────────────┐
|
|
296
|
+
│ Message │ ─── Event/action
|
|
297
|
+
└──────┬──────┘
|
|
298
|
+
│
|
|
299
|
+
▼
|
|
300
|
+
┌─────────────┐
|
|
301
|
+
│ Update │ ─── Modifies state
|
|
302
|
+
└──────┬──────┘
|
|
303
|
+
│
|
|
304
|
+
▼
|
|
305
|
+
┌─────────────┐
|
|
306
|
+
│ Model │ ─── Application state
|
|
307
|
+
└─────────────┘
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
### Setup
|
|
311
|
+
|
|
312
|
+
```toml
|
|
313
|
+
[dependencies]
|
|
314
|
+
iced = "0.12"
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
### Basic Application
|
|
318
|
+
|
|
319
|
+
```rust
|
|
320
|
+
use iced::{
|
|
321
|
+
widget::{button, column, text, text_input},
|
|
322
|
+
Element, Sandbox, Settings,
|
|
323
|
+
};
|
|
324
|
+
|
|
325
|
+
pub fn main() -> iced::Result {
|
|
326
|
+
Counter::run(Settings::default())
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
struct Counter {
|
|
330
|
+
value: i32,
|
|
331
|
+
input: String,
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
#[derive(Debug, Clone)]
|
|
335
|
+
enum Message {
|
|
336
|
+
Increment,
|
|
337
|
+
Decrement,
|
|
338
|
+
Reset,
|
|
339
|
+
InputChanged(String),
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
impl Sandbox for Counter {
|
|
343
|
+
type Message = Message;
|
|
344
|
+
|
|
345
|
+
fn new() -> Self {
|
|
346
|
+
Self {
|
|
347
|
+
value: 0,
|
|
348
|
+
input: String::new(),
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
fn title(&self) -> String {
|
|
353
|
+
String::from("Counter - Iced")
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
fn update(&mut self, message: Message) {
|
|
357
|
+
match message {
|
|
358
|
+
Message::Increment => {
|
|
359
|
+
self.value += 1;
|
|
360
|
+
}
|
|
361
|
+
Message::Decrement => {
|
|
362
|
+
self.value -= 1;
|
|
363
|
+
}
|
|
364
|
+
Message::Reset => {
|
|
365
|
+
self.value = 0;
|
|
366
|
+
}
|
|
367
|
+
Message::InputChanged(value) => {
|
|
368
|
+
self.input = value;
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
fn view(&self) -> Element<Message> {
|
|
374
|
+
column![
|
|
375
|
+
button("Increment").on_press(Message::Increment),
|
|
376
|
+
text(self.value).size(50),
|
|
377
|
+
button("Decrement").on_press(Message::Decrement),
|
|
378
|
+
button("Reset").on_press(Message::Reset),
|
|
379
|
+
text_input("Type something...", &self.input)
|
|
380
|
+
.on_input(Message::InputChanged),
|
|
381
|
+
]
|
|
382
|
+
.padding(20)
|
|
383
|
+
.into()
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
### Advanced Application with Commands
|
|
389
|
+
|
|
390
|
+
```rust
|
|
391
|
+
use iced::{Application, Command, Element, Settings, Theme};
|
|
392
|
+
use iced::widget::{button, column, text};
|
|
393
|
+
|
|
394
|
+
pub fn main() -> iced::Result {
|
|
395
|
+
MyApp::run(Settings::default())
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
struct MyApp {
|
|
399
|
+
state: AppState,
|
|
400
|
+
data: Option<String>,
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
enum AppState {
|
|
404
|
+
Idle,
|
|
405
|
+
Loading,
|
|
406
|
+
Loaded,
|
|
407
|
+
Error(String),
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
#[derive(Debug, Clone)]
|
|
411
|
+
enum Message {
|
|
412
|
+
LoadData,
|
|
413
|
+
DataLoaded(Result<String, String>),
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
impl Application for MyApp {
|
|
417
|
+
type Executor = iced::executor::Default;
|
|
418
|
+
type Message = Message;
|
|
419
|
+
type Theme = Theme;
|
|
420
|
+
type Flags = ();
|
|
421
|
+
|
|
422
|
+
fn new(_flags: ()) -> (Self, Command<Message>) {
|
|
423
|
+
(
|
|
424
|
+
Self {
|
|
425
|
+
state: AppState::Idle,
|
|
426
|
+
data: None,
|
|
427
|
+
},
|
|
428
|
+
Command::none(),
|
|
429
|
+
)
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
fn title(&self) -> String {
|
|
433
|
+
String::from("Async App - Iced")
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
fn update(&mut self, message: Message) -> Command<Message> {
|
|
437
|
+
match message {
|
|
438
|
+
Message::LoadData => {
|
|
439
|
+
self.state = AppState::Loading;
|
|
440
|
+
Command::perform(fetch_data(), Message::DataLoaded)
|
|
441
|
+
}
|
|
442
|
+
Message::DataLoaded(Ok(data)) => {
|
|
443
|
+
self.state = AppState::Loaded;
|
|
444
|
+
self.data = Some(data);
|
|
445
|
+
Command::none()
|
|
446
|
+
}
|
|
447
|
+
Message::DataLoaded(Err(error)) => {
|
|
448
|
+
self.state = AppState::Error(error);
|
|
449
|
+
Command::none()
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
fn view(&self) -> Element<Message> {
|
|
455
|
+
let content = match &self.state {
|
|
456
|
+
AppState::Idle => {
|
|
457
|
+
column![button("Load Data").on_press(Message::LoadData)]
|
|
458
|
+
}
|
|
459
|
+
AppState::Loading => {
|
|
460
|
+
column![text("Loading...")]
|
|
461
|
+
}
|
|
462
|
+
AppState::Loaded => {
|
|
463
|
+
column![
|
|
464
|
+
text(self.data.as_ref().unwrap()),
|
|
465
|
+
button("Reload").on_press(Message::LoadData),
|
|
466
|
+
]
|
|
467
|
+
}
|
|
468
|
+
AppState::Error(error) => {
|
|
469
|
+
column![
|
|
470
|
+
text(format!("Error: {}", error)),
|
|
471
|
+
button("Retry").on_press(Message::LoadData),
|
|
472
|
+
]
|
|
473
|
+
}
|
|
474
|
+
};
|
|
475
|
+
|
|
476
|
+
content.padding(20).into()
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
fn theme(&self) -> Theme {
|
|
480
|
+
Theme::Dark
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
async fn fetch_data() -> Result<String, String> {
|
|
485
|
+
tokio::time::sleep(tokio::time::Duration::from_secs(2)).await;
|
|
486
|
+
Ok("Data loaded successfully!".to_string())
|
|
487
|
+
}
|
|
488
|
+
```
|
|
489
|
+
|
|
490
|
+
### Custom Widgets in iced
|
|
491
|
+
|
|
492
|
+
```rust
|
|
493
|
+
use iced::widget::canvas::{self, Cache, Canvas, Cursor, Frame, Geometry, Path};
|
|
494
|
+
use iced::{Color, Element, Length, Point, Rectangle, Size, Theme};
|
|
495
|
+
|
|
496
|
+
struct CircleWidget {
|
|
497
|
+
cache: Cache,
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
impl CircleWidget {
|
|
501
|
+
fn new() -> Self {
|
|
502
|
+
Self {
|
|
503
|
+
cache: Cache::default(),
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
impl<Message> canvas::Program<Message> for CircleWidget {
|
|
509
|
+
type State = ();
|
|
510
|
+
|
|
511
|
+
fn draw(
|
|
512
|
+
&self,
|
|
513
|
+
_state: &(),
|
|
514
|
+
renderer: &iced::Renderer,
|
|
515
|
+
_theme: &Theme,
|
|
516
|
+
bounds: Rectangle,
|
|
517
|
+
_cursor: Cursor,
|
|
518
|
+
) -> Vec<Geometry> {
|
|
519
|
+
let geometry = self.cache.draw(renderer, bounds.size(), |frame| {
|
|
520
|
+
let center = frame.center();
|
|
521
|
+
let radius = frame.width().min(frame.height()) / 4.0;
|
|
522
|
+
|
|
523
|
+
let circle = Path::circle(center, radius);
|
|
524
|
+
frame.fill(&circle, Color::from_rgb(0.0, 0.5, 1.0));
|
|
525
|
+
});
|
|
526
|
+
|
|
527
|
+
vec![geometry]
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
// Usage in view
|
|
532
|
+
fn view(&self) -> Element<Message> {
|
|
533
|
+
Canvas::new(CircleWidget::new())
|
|
534
|
+
.width(Length::Fill)
|
|
535
|
+
.height(Length::Fill)
|
|
536
|
+
.into()
|
|
537
|
+
}
|
|
538
|
+
```
|
|
539
|
+
|
|
540
|
+
### Styling in iced
|
|
541
|
+
|
|
542
|
+
```rust
|
|
543
|
+
use iced::widget::button::{self, Button};
|
|
544
|
+
use iced::widget::container::{self, Container};
|
|
545
|
+
use iced::{Background, Border, Color, Theme};
|
|
546
|
+
|
|
547
|
+
struct CustomButtonStyle;
|
|
548
|
+
|
|
549
|
+
impl button::StyleSheet for CustomButtonStyle {
|
|
550
|
+
type Style = Theme;
|
|
551
|
+
|
|
552
|
+
fn active(&self, _style: &Self::Style) -> button::Appearance {
|
|
553
|
+
button::Appearance {
|
|
554
|
+
background: Some(Background::Color(Color::from_rgb(0.2, 0.6, 1.0))),
|
|
555
|
+
text_color: Color::WHITE,
|
|
556
|
+
border: Border {
|
|
557
|
+
radius: 5.0.into(),
|
|
558
|
+
..Default::default()
|
|
559
|
+
},
|
|
560
|
+
..Default::default()
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
fn hovered(&self, style: &Self::Style) -> button::Appearance {
|
|
565
|
+
let active = self.active(style);
|
|
566
|
+
|
|
567
|
+
button::Appearance {
|
|
568
|
+
background: Some(Background::Color(Color::from_rgb(0.3, 0.7, 1.0))),
|
|
569
|
+
..active
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
// Usage
|
|
575
|
+
button("Custom Styled Button").style(CustomButtonStyle)
|
|
576
|
+
```
|
|
577
|
+
|
|
578
|
+
## slint - Declarative UI
|
|
579
|
+
|
|
580
|
+
### Architecture
|
|
581
|
+
|
|
582
|
+
Slint uses a declarative markup language (.slint files) compiled to Rust code.
|
|
583
|
+
|
|
584
|
+
### Setup
|
|
585
|
+
|
|
586
|
+
```toml
|
|
587
|
+
[dependencies]
|
|
588
|
+
slint = "1.5"
|
|
589
|
+
|
|
590
|
+
[build-dependencies]
|
|
591
|
+
slint-build = "1.5"
|
|
592
|
+
```
|
|
593
|
+
|
|
594
|
+
**build.rs:**
|
|
595
|
+
```rust
|
|
596
|
+
fn main() {
|
|
597
|
+
slint_build::compile("ui/app.slint").unwrap();
|
|
598
|
+
}
|
|
599
|
+
```
|
|
600
|
+
|
|
601
|
+
### Basic Application
|
|
602
|
+
|
|
603
|
+
**ui/app.slint:**
|
|
604
|
+
```slint
|
|
605
|
+
import { Button, VerticalBox, HorizontalBox, LineEdit } from "std-widgets.slint";
|
|
606
|
+
|
|
607
|
+
export component App inherits Window {
|
|
608
|
+
in-out property<int> counter: 0;
|
|
609
|
+
in-out property<string> name: "World";
|
|
610
|
+
|
|
611
|
+
VerticalBox {
|
|
612
|
+
Text {
|
|
613
|
+
text: "Counter: \{counter}";
|
|
614
|
+
font-size: 24px;
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
HorizontalBox {
|
|
618
|
+
Button {
|
|
619
|
+
text: "Increment";
|
|
620
|
+
clicked => {
|
|
621
|
+
counter += 1;
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
Button {
|
|
626
|
+
text: "Decrement";
|
|
627
|
+
clicked => {
|
|
628
|
+
counter -= 1;
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
LineEdit {
|
|
634
|
+
placeholder-text: "Enter name";
|
|
635
|
+
text <=> name;
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
Text {
|
|
639
|
+
text: "Hello, \{name}!";
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
```
|
|
644
|
+
|
|
645
|
+
**main.rs:**
|
|
646
|
+
```rust
|
|
647
|
+
slint::include_modules!();
|
|
648
|
+
|
|
649
|
+
fn main() {
|
|
650
|
+
let app = App::new().unwrap();
|
|
651
|
+
|
|
652
|
+
// Access properties
|
|
653
|
+
app.set_counter(10);
|
|
654
|
+
println!("Counter: {}", app.get_counter());
|
|
655
|
+
|
|
656
|
+
// Run application
|
|
657
|
+
app.run().unwrap();
|
|
658
|
+
}
|
|
659
|
+
```
|
|
660
|
+
|
|
661
|
+
### Advanced slint Features
|
|
662
|
+
|
|
663
|
+
**Callbacks:**
|
|
664
|
+
```slint
|
|
665
|
+
export component App inherits Window {
|
|
666
|
+
callback button-clicked(string);
|
|
667
|
+
|
|
668
|
+
Button {
|
|
669
|
+
text: "Click me";
|
|
670
|
+
clicked => {
|
|
671
|
+
button-clicked("Button was clicked!");
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
```
|
|
676
|
+
|
|
677
|
+
```rust
|
|
678
|
+
slint::include_modules!();
|
|
679
|
+
|
|
680
|
+
fn main() {
|
|
681
|
+
let app = App::new().unwrap();
|
|
682
|
+
|
|
683
|
+
app.on_button_clicked(|msg| {
|
|
684
|
+
println!("Callback: {}", msg);
|
|
685
|
+
});
|
|
686
|
+
|
|
687
|
+
app.run().unwrap();
|
|
688
|
+
}
|
|
689
|
+
```
|
|
690
|
+
|
|
691
|
+
**Custom Structs:**
|
|
692
|
+
```slint
|
|
693
|
+
export struct Person {
|
|
694
|
+
name: string,
|
|
695
|
+
age: int,
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
export component App inherits Window {
|
|
699
|
+
in-out property<Person> user: { name: "Alice", age: 30 };
|
|
700
|
+
|
|
701
|
+
Text {
|
|
702
|
+
text: "\{user.name} is \{user.age} years old";
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
```
|
|
706
|
+
|
|
707
|
+
```rust
|
|
708
|
+
use slint::Model;
|
|
709
|
+
|
|
710
|
+
slint::include_modules!();
|
|
711
|
+
|
|
712
|
+
fn main() {
|
|
713
|
+
let app = App::new().unwrap();
|
|
714
|
+
|
|
715
|
+
let person = Person {
|
|
716
|
+
name: "Bob".into(),
|
|
717
|
+
age: 25,
|
|
718
|
+
};
|
|
719
|
+
app.set_user(person);
|
|
720
|
+
|
|
721
|
+
app.run().unwrap();
|
|
722
|
+
}
|
|
723
|
+
```
|
|
724
|
+
|
|
725
|
+
**Lists and Models:**
|
|
726
|
+
```slint
|
|
727
|
+
export component App inherits Window {
|
|
728
|
+
in-out property<[string]> items: ["Item 1", "Item 2", "Item 3"];
|
|
729
|
+
|
|
730
|
+
VerticalBox {
|
|
731
|
+
for item in items: Text {
|
|
732
|
+
text: item;
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
```
|
|
737
|
+
|
|
738
|
+
```rust
|
|
739
|
+
use slint::{Model, ModelRc, VecModel};
|
|
740
|
+
|
|
741
|
+
slint::include_modules!();
|
|
742
|
+
|
|
743
|
+
fn main() {
|
|
744
|
+
let app = App::new().unwrap();
|
|
745
|
+
|
|
746
|
+
let model = Rc::new(VecModel::from(vec![
|
|
747
|
+
"Dynamic Item 1".into(),
|
|
748
|
+
"Dynamic Item 2".into(),
|
|
749
|
+
]));
|
|
750
|
+
|
|
751
|
+
app.set_items(ModelRc::from(model.clone()));
|
|
752
|
+
|
|
753
|
+
app.run().unwrap();
|
|
754
|
+
}
|
|
755
|
+
```
|
|
756
|
+
|
|
757
|
+
## druid (Archived - Reference Only)
|
|
758
|
+
|
|
759
|
+
Druid is in maintenance mode but offers valuable patterns for data-driven UIs.
|
|
760
|
+
|
|
761
|
+
### Core Concepts
|
|
762
|
+
|
|
763
|
+
**Lens Pattern:**
|
|
764
|
+
```rust
|
|
765
|
+
use druid::widget::{Button, Flex, Label, TextBox};
|
|
766
|
+
use druid::{AppLauncher, Data, Lens, Widget, WindowDesc};
|
|
767
|
+
|
|
768
|
+
#[derive(Clone, Data, Lens)]
|
|
769
|
+
struct AppState {
|
|
770
|
+
name: String,
|
|
771
|
+
count: u32,
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
fn build_ui() -> impl Widget<AppState> {
|
|
775
|
+
Flex::column()
|
|
776
|
+
.with_child(Label::new(|data: &AppState, _env: &_| {
|
|
777
|
+
format!("Hello, {}!", data.name)
|
|
778
|
+
}))
|
|
779
|
+
.with_child(TextBox::new().lens(AppState::name))
|
|
780
|
+
.with_child(Label::new(|data: &AppState, _env: &_| {
|
|
781
|
+
format!("Count: {}", data.count)
|
|
782
|
+
}))
|
|
783
|
+
.with_child(Button::new("Increment").on_click(|_ctx, data: &mut AppState, _env| {
|
|
784
|
+
data.count += 1;
|
|
785
|
+
}))
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
fn main() {
|
|
789
|
+
let main_window = WindowDesc::new(build_ui())
|
|
790
|
+
.title("Druid App")
|
|
791
|
+
.window_size((400.0, 300.0));
|
|
792
|
+
|
|
793
|
+
let initial_state = AppState {
|
|
794
|
+
name: "World".to_string(),
|
|
795
|
+
count: 0,
|
|
796
|
+
};
|
|
797
|
+
|
|
798
|
+
AppLauncher::with_window(main_window)
|
|
799
|
+
.launch(initial_state)
|
|
800
|
+
.expect("Failed to launch application");
|
|
801
|
+
}
|
|
802
|
+
```
|
|
803
|
+
|
|
804
|
+
## Framework Selection Guide
|
|
805
|
+
|
|
806
|
+
### Decision Tree
|
|
807
|
+
|
|
808
|
+
```
|
|
809
|
+
Choose GUI framework based on:
|
|
810
|
+
|
|
811
|
+
Project Type:
|
|
812
|
+
├─ Game editor, debug tools → egui
|
|
813
|
+
├─ Cross-platform app with complex state → iced
|
|
814
|
+
├─ Embedded device, touch interface → slint
|
|
815
|
+
└─ Data-driven (consider alternatives) → druid/iced
|
|
816
|
+
|
|
817
|
+
Development Speed:
|
|
818
|
+
├─ Rapid prototyping → egui
|
|
819
|
+
├─ Type-safe architecture → iced
|
|
820
|
+
└─ Designer collaboration → slint
|
|
821
|
+
|
|
822
|
+
Performance Needs:
|
|
823
|
+
├─ 60+ FPS immediate updates → egui
|
|
824
|
+
├─ GPU-accelerated rendering → iced/slint
|
|
825
|
+
└─ Minimal CPU usage → slint
|
|
826
|
+
|
|
827
|
+
Team Experience:
|
|
828
|
+
├─ Immediate mode GUI → egui
|
|
829
|
+
├─ Elm/functional programming → iced
|
|
830
|
+
└─ QML/declarative UI → slint
|
|
831
|
+
```
|
|
832
|
+
|
|
833
|
+
### Migration Paths
|
|
834
|
+
|
|
835
|
+
**From web to native:**
|
|
836
|
+
- Tauri → egui: Extract backend, rebuild UI
|
|
837
|
+
- React → iced: Messages ≈ Actions, State ≈ Model
|
|
838
|
+
|
|
839
|
+
**Between native frameworks:**
|
|
840
|
+
- egui → iced: Refactor to Elm architecture
|
|
841
|
+
- iced → slint: Extract logic, rebuild in .slint
|
|
842
|
+
|
|
843
|
+
## Production Tips
|
|
844
|
+
|
|
845
|
+
### Performance
|
|
846
|
+
|
|
847
|
+
**egui:**
|
|
848
|
+
- Use `ui.ctx().request_repaint()` sparingly
|
|
849
|
+
- Cache expensive computations
|
|
850
|
+
- Profile with `puffin` profiler
|
|
851
|
+
|
|
852
|
+
**iced:**
|
|
853
|
+
- Minimize `Command` usage
|
|
854
|
+
- Use `subscription` for continuous updates
|
|
855
|
+
- Batch state updates
|
|
856
|
+
|
|
857
|
+
**slint:**
|
|
858
|
+
- Use `property` bindings efficiently
|
|
859
|
+
- Optimize model updates
|
|
860
|
+
- Profile with built-in tools
|
|
861
|
+
|
|
862
|
+
### Distribution
|
|
863
|
+
|
|
864
|
+
All frameworks support:
|
|
865
|
+
- Static binaries (3-10MB)
|
|
866
|
+
- Cross-compilation
|
|
867
|
+
- Native installers (MSI, DMG, DEB)
|
|
868
|
+
|
|
869
|
+
### Testing
|
|
870
|
+
|
|
871
|
+
**egui:**
|
|
872
|
+
```rust
|
|
873
|
+
#[cfg(test)]
|
|
874
|
+
mod tests {
|
|
875
|
+
use super::*;
|
|
876
|
+
|
|
877
|
+
#[test]
|
|
878
|
+
fn test_app_logic() {
|
|
879
|
+
let mut app = MyApp::default();
|
|
880
|
+
// Test state changes
|
|
881
|
+
assert_eq!(app.counter, 0);
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
```
|
|
885
|
+
|
|
886
|
+
**iced:**
|
|
887
|
+
```rust
|
|
888
|
+
#[cfg(test)]
|
|
889
|
+
mod tests {
|
|
890
|
+
use super::*;
|
|
891
|
+
|
|
892
|
+
#[test]
|
|
893
|
+
fn test_update() {
|
|
894
|
+
let mut app = Counter::new();
|
|
895
|
+
app.update(Message::Increment);
|
|
896
|
+
assert_eq!(app.value, 1);
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
```
|
|
900
|
+
|
|
901
|
+
This guide covers production-ready patterns for all major Rust GUI frameworks. Choose based on project needs, team skills, and performance requirements.
|