Slint Kit Documentation

Couleurs

Sélecteur de couleur pour Slint

KitColorPicker · Slint Kit 0.3.0

Éditeur de couleur avec texte hexadécimal, champs de canaux et curseurs.

Éditeur de couleur avec texte hexadécimal, champs de canaux et curseurs.
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.

Modifiez une couleur sRGB hexadécimale ou utilisez le sélecteur. Rust valide les octets, sépare les brouillons et projette la couleur acceptée.

ui/app.slint

export { KitFormValidation, HtmlInputKind, NumberInputFormat, KitInputConstraints, KitValidationResult, KitValidityState } from "@kit/kit.slint";
import {KitColorPicker,KitThemes,SrgbColorView,ColorChannel,NavigationStep} from "@kit/kit.slint";
export {SrgbColorView,ColorChannel,NavigationStep}
export component App inherits Window {
 title:"color-picker";preferred-width:540px;preferred-height:800px;max-width:100000px;max-height:100000px;background:KitThemes.light.canvas;
 in-out property <string> hex;in property <string> error;in property <string> accepted;in property <SrgbColorView> color-value;in property <color> swatch;
 callback commit(string);callback edit();callback choose(SrgbColorView);callback cancel();in-out property <string> red;in-out property <string> green;in-out property <string> blue;in-out property <string> alpha;
 callback channel(ColorChannel,string);callback position(ColorChannel,float);callback navigate(ColorChannel,NavigationStep);callback begin-gesture(ColorChannel);callback finish(ColorChannel);callback cancel-channel(ColorChannel);
 VerticalLayout{padding:24px;spacing:16px;alignment:start;
 control:=KitColorPicker{label:"Accent color";accepted-color:root.color-value;swatch:root.swatch;color-requested(value)=>{root.choose(value);}hex-text<=>root.hex;red-text<=>root.red;green-text<=>root.green;blue-text<=>root.blue;alpha-text<=>root.alpha;hex-error:root.error;
 hex-edited(value)=>{root.edit();}hex-commit-requested(value)=>{root.commit(value);}
 channel-edited(channel,value)=>{root.edit();}channel-commit-requested(channel,value)=>{root.channel(channel,value);}
 channel-position-requested(channel,position)=>{root.position(channel,position);}channel-navigate(channel,step)=>{root.navigate(channel,step);}
 gesture-started(channel)=>{root.begin-gesture(channel);}committed(channel)=>{root.finish(channel);}cancelled(channel)=>{root.cancel-channel(channel);}}
 Text{text:"Accepted: "+root.accepted;color:KitThemes.light.text-primary;wrap:word-wrap;}
 }
}

src/main.rs

use slint::ComponentHandle;
use slint_kit::{AlphaPolicy, DraftValue, SrgbColor};
use std::{cell::RefCell, rc::Rc};
mod ui {
    slint::include_modules!();
}
fn publish(app: &ui::App, color: SrgbColor, accepted: bool) {
    app.set_hex(color.hex(AlphaPolicy::Opaque).into());
    app.set_color_value(ui::SrgbColorView {
        red: color.red.into(),
        green: color.green.into(),
        blue: color.blue.into(),
        alpha: 255,
    });
    app.set_swatch(slint::Color::from_rgb_u8(
        color.red,
        color.green,
        color.blue,
    ));
    if accepted {
        app.set_accepted(color.hex(AlphaPolicy::Opaque).into());
    }
    app.set_red(color.red.to_string().into());
    app.set_green(color.green.to_string().into());
    app.set_blue(color.blue.to_string().into());
    app.set_alpha("255".into());
}
fn main() -> Result<(), slint::PlatformError> {
    let app = ui::App::new()?;
    slint_kit::init_form_validation!(ui, app);
    let initial = SrgbColor {
        red: 79,
        green: 70,
        blue: 229,
        alpha: 255,
    };
    let state = Rc::new(RefCell::new((initial, initial)));
    publish(&app, initial, true);
    let weak = app.as_weak();
    let shared = state.clone();
    app.on_commit(move |text| {
        if let Some(app) = weak.upgrade() {
            match SrgbColor::parse_hex(&text, AlphaPolicy::Opaque) {
                DraftValue::Parsed(value) => {
                    *shared.borrow_mut() = (value, value);
                    publish(&app, value, true);
                    app.set_error("".into());
                }
                _ => app.set_error("Use #RRGGBB or #RGB hexadecimal".into()),
            }
        }
    });
    let weak = app.as_weak();
    app.on_edit(move || {
        if let Some(app) = weak.upgrade() {
            app.set_error("".into());
        }
    });
    let weak = app.as_weak();
    let shared = state.clone();
    app.on_choose(move |view| {
        if let Some(app) = weak.upgrade() {
            if let (Ok(red), Ok(green), Ok(blue)) = (
                view.red.try_into(),
                view.green.try_into(),
                view.blue.try_into(),
            ) {
                let color = SrgbColor {
                    red,
                    green,
                    blue,
                    alpha: 255,
                };
                *shared.borrow_mut() = (color, color);
                publish(&app, color, true);
                app.set_error("".into());
            }
        }
    });

    fn set(color: &mut SrgbColor, channel: ui::ColorChannel, value: u8) {
        match channel {
            ui::ColorChannel::Red => color.red = value,
            ui::ColorChannel::Green => color.green = value,
            ui::ColorChannel::Blue => color.blue = value,
            ui::ColorChannel::Alpha => {}
        }
    }
    fn get(color: SrgbColor, channel: ui::ColorChannel) -> u8 {
        match channel {
            ui::ColorChannel::Red => color.red,
            ui::ColorChannel::Green => color.green,
            ui::ColorChannel::Blue => color.blue,
            ui::ColorChannel::Alpha => 255,
        }
    }
    let weak = app.as_weak();
    let shared = state.clone();
    app.on_channel(move |_channel, _text| {
        if let Some(app) = weak.upgrade() {
            let channels = app
                .get_red()
                .parse::<u8>()
                .ok()
                .zip(app.get_green().parse::<u8>().ok())
                .zip(app.get_blue().parse::<u8>().ok());
            if let Some(((red, green), blue)) = channels {
                let color = SrgbColor {
                    red,
                    green,
                    blue,
                    alpha: 255,
                };
                *shared.borrow_mut() = (color, color);
                publish(&app, color, true);
                app.set_error("".into());
            } else {
                app.set_error("Use whole sRGB channels from 0 to 255".into());
            }
        }
    });
    let weak = app.as_weak();
    let shared = state.clone();
    app.on_position(move |channel, position| {
        if let Some(app) = weak.upgrade() {
            let mut state = shared.borrow_mut();
            set(
                &mut state.1,
                channel,
                (position.clamp(0.0, 1.0) * 255.0).round() as u8,
            );
            publish(&app, state.1, false);
        }
    });
    let weak = app.as_weak();
    let shared = state.clone();
    app.on_navigate(move |channel, step| {
        if let Some(app) = weak.upgrade() {
            let mut state = shared.borrow_mut();
            let old = get(state.1, channel) as i32;
            let next = match step {
                ui::NavigationStep::First => 0,
                ui::NavigationStep::Last => 255,
                ui::NavigationStep::Next => old + 1,
                ui::NavigationStep::Previous => old - 1,
                ui::NavigationStep::PageNext => old + 16,
                ui::NavigationStep::PagePrevious => old - 16,
            };
            set(&mut state.1, channel, next.clamp(0, 255) as u8);
            state.0 = state.1;
            publish(&app, state.0, true);
        }
    });
    let shared = state.clone();
    app.on_begin_gesture(move |_| {
        let mut state = shared.borrow_mut();
        state.1 = state.0;
    });
    let weak = app.as_weak();
    let shared = state.clone();
    app.on_finish(move |_| {
        if let Some(app) = weak.upgrade() {
            let mut state = shared.borrow_mut();
            state.0 = state.1;
            publish(&app, state.0, true);
        }
    });
    let weak = app.as_weak();
    let shared = state.clone();
    app.on_cancel_channel(move |_| {
        if let Some(app) = weak.upgrade() {
            let mut state = shared.borrow_mut();
            state.1 = state.0;
            publish(&app, state.0, true);
            app.set_error("".into());
        }
    });

    app.run()
}

Cargo.toml et build.rs

Cargo.toml

[package]
name = "slint-kit-color-picker-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

Conservez SrgbColor accepté en Rust. Connectez validation des canaux, positions et début, validation et annulation des gestes.

L’exemple accepte les couleurs hexadécimales opaques et les canaux de 0 à 255. Annuler restaure la couleur enregistrée au début du geste.

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
Éditeur de couleur avec texte hexadécimal, champs de canaux et curseurs.
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 { KitColorPicker, KitTheme, KitThemes } from "@kit/kit.slint";

export component Example inherits Window {
    title: "KitColorPicker — exemple";
    preferred-width: 480px;
    preferred-height: 550px;
    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: "Sélecteur de couleur"; color: root.theme.text-primary; font-size: 20px * root.theme.text-scale; font-weight: 600; }
        KitColorPicker { theme: root.theme; label: "Couleur d’accent"; swatch: #6366f1; accepted-color: { red: 99, green: 102, blue: 241, alpha: 255 }; hex-text: "#6366F1"; red-text: "99"; green-text: "102"; blue-text: "241"; }
        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
control-size ControlSize in
access FieldAccess in
alpha-mode ColorAlphaMode in
accepted-color SrgbColorView in
swatch color in
label string in
hex-error string in
red-error string in
green-error string in
blue-error string in
alpha-error string in
hex-text string in-out
red-text string in-out
green-text string in-out
blue-text string in-out
alpha-text string in-out

Types et variantes

Type Déclaration
enum ColorAlphaMode opaque, editable
enum ColorChannel red, green, blue, alpha
enum ControlSize compact, medium, comfortable
enum FieldAccess editable, read-only, disabled
enum NavigationStep next, previous, first, last, page-next, page-previous
struct SrgbColorView red: int, green: int, blue: int, alpha: int

Signaux

Voir aussi