Slint Kit Documentation

Données virtualisées

Vue de texte virtualisée pour Slint

KitVirtualTextView · Slint Kit 0.3.1

Vue de document Slint avec sélection et édition native d’une ligne.

Vue de document Slint avec sélection et édition native d’une ligne.
Application native de l’exemple ci-dessous.

Essayer le composant

Télécharger le projet complet, le décompresser et lancer cargo run --locked dans son dossier. Rust 1.92+ ; Slint Kit 0.3.0 depuis crates.io. Vous pouvez aussi copier les quatre fichiers ci-dessous.

Sélectionnez une ligne, modifiez-la dans le champ natif, puis cliquez sur Save line. Rust met cette ligne à jour sans changer sa clé.

ui/app.slint

export { KitFormValidation, HtmlInputKind, NumberInputFormat, KitInputConstraints, KitValidationResult, KitValidityState } from "@kit/kit.slint";
// SPDX-License-Identifier: GPL-3.0-or-later
import { KitVirtualTextView, KitThemes, TextLine, TextViewAccess, NavigationStep } from "@kit/kit.slint";
export { TextLine, NavigationStep }
export component App inherits Window {
    title: "Slint virtual text view";
    preferred-width: 640px; preferred-height: 340px;
    max-width: 100000px; max-height: 100000px;
    background: KitThemes.light.canvas;
    in property <[TextLine]> lines;
    in property <int> active-index: -1;
    in-out property <string> draft;
    in property <string> status: "Select a line to edit";
    in property <string> error;
    callback choose(int, int);
    callback move(NavigationStep);
    callback save(int, int, string);
    callback copied(int, int);
    VerticalLayout {
        padding: 24px; spacing: 16px;
        document := KitVirtualTextView { height: 240px; lines: root.lines; generation: 1;
            active-index: root.active-index; access: TextViewAccess.edit-line;
            line-draft <=> root.draft; error: root.error;
            select-line(token, generation) => { root.choose(token, generation); }
            navigate(step) => { root.move(step); }
            commit-line(token, generation, text) => { root.save(token, generation, text); }
            copy-requested(token, generation) => { root.copied(token, generation); }
        }
        Text { text: root.status; color: KitThemes.light.text-primary; }
    }
}

src/main.rs

// SPDX-License-Identifier: GPL-3.0-or-later
use slint::{ComponentHandle, ModelRc, VecModel};
use std::{cell::RefCell, rc::Rc};
mod ui {
    slint::include_modules!();
}

fn next_index(current: Option<usize>, length: usize, step: ui::NavigationStep) -> usize {
    use ui::NavigationStep::*;
    let current = current.unwrap_or(0);
    match step {
        First => 0,
        Last => length.saturating_sub(1),
        Previous | PagePrevious => current.saturating_sub(1),
        Next | PageNext => (current + 1).min(length.saturating_sub(1)),
    }
}

#[derive(Clone, Copy, PartialEq, Eq)]
enum LineKey {
    Heading,
    Body,
    Closing,
}
impl LineKey {
    fn token(self) -> i32 {
        match self {
            Self::Heading => 11,
            Self::Body => 23,
            Self::Closing => 37,
        }
    }
}
struct Line {
    key: LineKey,
    text: String,
}
struct State {
    lines: Vec<Line>,
    selected: Option<LineKey>,
}
fn select(state: &State, app: &ui::App) {
    let index = state
        .lines
        .iter()
        .position(|line| Some(line.key) == state.selected);
    let draft = index.map_or("", |i| state.lines[i].text.as_str());
    app.set_active_index(index.map_or(-1, |i| i as i32));
    app.set_draft(draft.into());
    app.set_error("".into());
}
fn publish(state: &RefCell<State>, app: &ui::App) {
    let state = state.borrow();
    let lines = state
        .lines
        .iter()
        .enumerate()
        .map(|(index, line)| ui::TextLine {
            token: line.key.token(),
            number: index as i32 + 1,
            text: line.text.as_str().into(),
            selected: state.selected == Some(line.key),
        })
        .collect::<Vec<_>>();
    drop(state);
    app.set_lines(ModelRc::new(VecModel::from(lines)));
}
fn main() -> Result<(), slint::PlatformError> {
    let app = ui::App::new()?;
    slint_kit::init_form_validation!(ui, app);
    let state = Rc::new(RefCell::new(State {
        lines: vec![
            Line {
                key: LineKey::Heading,
                text: "# Project notes".into(),
            },
            Line {
                key: LineKey::Body,
                text: "Select and edit this line.".into(),
            },
            Line {
                key: LineKey::Closing,
                text: "Rust owns the saved text.".into(),
            },
        ],
        selected: None,
    }));
    publish(&state, &app);
    let (model, weak) = (state.clone(), app.as_weak());
    app.on_choose(move |token, generation| {
        if generation != 1 {
            return;
        }
        {
            let mut state = model.borrow_mut();
            let Some(key) = state
                .lines
                .iter()
                .find(|line| line.key.token() == token)
                .map(|line| line.key)
            else {
                return;
            };
            state.selected = Some(key);
        }
        if let Some(app) = weak.upgrade() {
            select(&model.borrow(), &app);
            publish(&model, &app);
        }
    });
    let (model, weak) = (state.clone(), app.as_weak());
    app.on_move(move |step| {
        {
            let mut state = model.borrow_mut();
            let current = state
                .lines
                .iter()
                .position(|line| Some(line.key) == state.selected);
            let index = next_index(current, state.lines.len(), step);
            state.selected = Some(state.lines[index].key);
        }
        if let Some(app) = weak.upgrade() {
            select(&model.borrow(), &app);
            publish(&model, &app);
        }
    });
    let (model, weak) = (state.clone(), app.as_weak());
    app.on_save(move |token, generation, text| {
        if generation != 1 {
            return;
        }
        let Some(app) = weak.upgrade() else {
            return;
        };
        if text.contains(['\n', '\r']) {
            app.set_error("Enter one line only".into());
            return;
        }
        {
            let mut state = model.borrow_mut();
            if state.selected.map(LineKey::token) != Some(token) {
                return;
            }
            let Some(line) = state
                .lines
                .iter_mut()
                .find(|line| line.key.token() == token)
            else {
                return;
            };
            line.text = text.to_string();
        }
        app.set_error("".into());
        app.set_status("Line saved in Rust".into());
        publish(&model, &app);
    });
    let weak = app.as_weak();
    app.on_copied(move |_token, generation| {
        if generation == 1 {
            if let Some(app) = weak.upgrade() {
                app.set_status("Selected line copied".into());
            }
        }
    });
    app.run()
}

Cargo.toml et build.rs

Cargo.toml

[package]
name = "slint-kit-virtual-text-view-example"
version = "0.1.0"
edition = "2024"
rust-version = "1.92"
license = "GPL-3.0-or-later"
publish = false

[dependencies]
slint-kit = "=0.3.0"
slint = { version = "=1.18.1", default-features = false, features = ["std", "compat-1-18", "backend-winit", "renderer-skia", "accessibility"] }

[build-dependencies]
slint-kit-build = "=0.3.0"

[workspace]

build.rs

fn main() {
    slint_kit_build::compile("ui/app.slint").expect("compile component example");
}

Fichiers : Cargo.toml · build.rs · ui/app.slint · src/main.rs

Fournissez lines, generation et active-index. Choisissez access: TextViewAccess.edit-line, liez line-draft et enregistrez le texte accepté dans Rust via commit-line(token, generation, text).

Traitez select-line pour charger le brouillon sélectionné et navigate pour changer de ligne. copy-requested signale une copie. Utilisez KitTextArea pour éditer un tampon multiligne complet.

Choisir entre une option intégrée et une composition personnalisée

Options partagées

Composition visuelle

Le fichier ci-dessous produit la capture. Pour les composants pilotés par un contrôleur, il montre l’état affiché sans fournir le branchement Rust. Créer un projet.

Afficher le fichier de la capture
Vue de document Slint avec sélection et édition native d’une ligne.
Capture native du même exemple, moteur Skia.
// Public label overrides localize this fixture; toolkit/host defaults keep their language.
// SPDX-License-Identifier: GPL-3.0-or-later
import { KitTheme, KitThemes, KitVirtualTextView } from "@kit/kit.slint";

export component Example inherits Window {
    title: "KitVirtualTextView — exemple";
    preferred-width: 620px;
    preferred-height: 360px;
    max-width: 100000px;
    max-height: 100000px;
    background: root.theme.canvas;
    in-out property <KitTheme> theme: KitThemes.light;
    in-out property <string> status;
    VerticalLayout {
        padding: 24px; spacing: 16px; alignment: start;
        Text { text: "Vue de texte virtualisée"; color: root.theme.text-primary; font-size: 20px * root.theme.text-scale; font-weight: 600; }
        KitVirtualTextView { label: "Document texte"; theme: root.theme; height: 230px; generation: 1; lines: [{ token: 101, number: 1, text: "# Notes de terrain", selected: false }, { token: 205, number: 2, text: "Un espace partagé pour les observations.", selected: true }, { token: 309, number: 3, text: "Préservez les identités lorsque les lignes changent de position.", selected: false }, { token: 413, number: 4, text: "Utilisez l’édition native pour les lignes sélectionnées.", selected: false }]; }
        if root.status != "": Text { text: root.status; color: root.theme.text-secondary; font-size: 12px * root.theme.text-scale; wrap: word-wrap; }
    }
}

Télécharger cet exemple

API publique

Hérite de Rectangle.

Propriétés

Nom Type Sens
theme KitTheme in
lines [TextLine] in
generation int in
active-index int in
access TextViewAccess in
line-draft string in-out
error string in
label string in
scroll-y length out

Types et variantes

Type Déclaration
enum NavigationStep next, previous, first, last, page-next, page-previous
struct TextLine token: int, number: int, text: string, selected: bool
enum TextViewAccess read-only, edit-line

Signaux

Fonctions

Voir aussi