iced_tour
Guided tour / onboarding overlay for iced applications.
Built for and extracted from Telemetry Studio, a desktop app for cycling video overlays.

Installation
Or add to your Cargo.toml:
[]
= "0.1"
Features
- Widget ID targeting — spotlight any widget by container ID, resolved at runtime on any screen size
- Smooth animations — spotlight slides between targets with configurable easing and duration
- Multiple named tours — manage separate tours (e.g. welcome, editor) with independent completion tracking
- Keyboard navigation — Escape to skip, arrow keys to navigate, Enter for next
- Lifecycle events —
StepEntered,StepExited,Completed,Skippedfor triggering side effects - Full-screen backdrop with spotlight cutout and rounded corners
- Tooltip card with title, description, dot indicators, and navigation buttons
- Dark and light theme presets with full customization
- Builder pattern API for defining tour steps
tour_steps![]convenience macro for quick definitions- Zero-cost when inactive (returns invisible
Space) - Works with iced's
stack![]composition — no custom overlay system needed
Quick Start
use ;
// 1. Define your steps
let steps = tour_steps!;
// 2. Create state (inactive by default)
let mut tour_state = new;
// 3. Start the tour when ready
tour_state.start;
Integration
Add to your App struct
use ;
Add to your Message enum
use TourManagerMessage;
use Rectangle;
Initialize with named tours
let tour_manager = new
.add_tour;
Add to your view (stack composition)
use tour_manager_overlay;
Handle messages in update
Tour Steps
Steps can highlight specific UI areas in three ways:
use ;
use ;
// 1. Centered card (no target — works without knowing layout positions)
new;
// 2. Widget ID targeting (recommended — resolved at runtime, works on any screen size)
new
.target_id
.card_position;
// 3. Manual rectangle (for panels with known dimensions)
new
.target
.card_position;
Widget ID Targeting
The recommended approach. Wrap your target widget in a container with an ID, and the spotlight will find it automatically:
// In your view(), tag widgets:
container.id
// In your step definition:
new
.target_id
This works on any screen size and adapts to layout changes — no pixel math needed.
Lifecycle Events
Use events from tour_manager.update(msg) to trigger side effects:
use ;
for event in self.tour_manager.update
Persisting Completion
Prevent the tour from showing again after the user completes or skips it:
// On app startup, restore completion state from your preferences:
if preferences.welcome_tour_completed
// On tour completion (in the event handler above):
// Save a boolean to your preferences file, database, etc.
Themes
use TourTheme;
// Presets
let dark = dark;
let light = light;
// Customize
let custom = dark
.with_fonts
.with_backdrop_opacity;
// Or set individual fields
let mut theme = dark;
theme.button_color = from_rgb;
API Reference
| Function / Type | Description |
|---|---|
TourManager::new() |
Create a multi-tour manager |
.add_tour(name, steps) |
Register a named tour |
.start(name) |
Start a named tour |
.is_completed(name) |
Check if a tour was completed |
.resolve_bounds_task(mapper) |
Get Task to resolve widget ID bounds |
TourStep::new(title, desc) |
Step with centered card (no cutout) |
.target_id(id) |
Spotlight a widget by container ID (recommended) |
.target(rect) |
Spotlight a manual rectangle |
.card_position(pos) |
Control card placement |
tour_manager_overlay(mgr, theme, mapper) |
Overlay for multi-tour manager |
tour_overlay(state, theme, mapper) |
Overlay for single tour |
tour_steps![...] |
Convenience macro |
TourTheme::dark() / light() |
Theme presets |
TourAnimation::default() / none() |
Animation config (300ms EaseOutCubic / disabled) |
Examples
Compatibility
- Minimum iced version: 0.14
- Minimum Rust version: 1.88
License
Licensed under either of
- Apache License, Version 2.0 (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
- MIT License (LICENSE-MIT or http://opensource.org/licenses/MIT)
at your option.