mirror of
https://github.com/slint-ui/slint.git
synced 2025-07-07 13:15:23 +00:00

After adding an Image in a builtin struct, some code and include need to be moved a bit around
1032 lines
37 KiB
Rust
1032 lines
37 KiB
Rust
// Copyright © SixtyFPS GmbH <info@slint.dev>
|
|
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0
|
|
|
|
use anyhow::Context;
|
|
use std::io::{BufWriter, Write};
|
|
use std::path::{Path, PathBuf};
|
|
|
|
// cSpell: ignore compat constexpr corelib deps sharedvector pathdata
|
|
|
|
fn enums(path: &Path) -> anyhow::Result<()> {
|
|
let mut enums_priv = BufWriter::new(
|
|
std::fs::File::create(path.join("slint_enums_internal.h"))
|
|
.context("Error creating slint_enums_internal.h file")?,
|
|
);
|
|
writeln!(enums_priv, "#pragma once")?;
|
|
writeln!(enums_priv, "// This file is auto-generated from {}", file!())?;
|
|
writeln!(enums_priv, "#include \"slint_enums.h\"")?;
|
|
writeln!(enums_priv, "namespace slint::cbindgen_private {{")?;
|
|
let mut enums_pub = BufWriter::new(
|
|
std::fs::File::create(path.join("slint_enums.h"))
|
|
.context("Error creating slint_enums.h file")?,
|
|
);
|
|
writeln!(enums_pub, "#pragma once")?;
|
|
writeln!(enums_pub, "// This file is auto-generated from {}", file!())?;
|
|
writeln!(enums_pub, "namespace slint {{")?;
|
|
macro_rules! enum_file {
|
|
(PointerEventButton) => {{
|
|
writeln!(enums_priv, "using slint::PointerEventButton;")?;
|
|
&mut enums_pub
|
|
}};
|
|
(AccessibleRole) => {{
|
|
writeln!(enums_priv, "using slint::testing::AccessibleRole;")?;
|
|
&mut enums_pub
|
|
}};
|
|
($_:ident) => {
|
|
&mut enums_priv
|
|
};
|
|
}
|
|
macro_rules! enum_sub_namespace {
|
|
(AccessibleRole) => {{
|
|
Some("testing")
|
|
}};
|
|
($_:ident) => {
|
|
None
|
|
};
|
|
}
|
|
macro_rules! print_enums {
|
|
($( $(#[doc = $enum_doc:literal])* $(#[non_exhaustive])? enum $Name:ident { $( $(#[doc = $value_doc:literal])* $Value:ident,)* })*) => {
|
|
$(
|
|
let file = enum_file!($Name);
|
|
let namespace: Option<&'static str> = enum_sub_namespace!($Name);
|
|
if let Some(ns) = namespace {
|
|
writeln!(file, "namespace {} {{", ns)?;
|
|
}
|
|
$(writeln!(file, "///{}", $enum_doc)?;)*
|
|
writeln!(file, "enum class {} {{", stringify!($Name))?;
|
|
$(
|
|
$(writeln!(file, " ///{}", $value_doc)?;)*
|
|
writeln!(file, " {},", stringify!($Value).trim_start_matches("r#"))?;
|
|
)*
|
|
writeln!(file, "}};")?;
|
|
if namespace.is_some() {
|
|
writeln!(file, "}}")?;
|
|
}
|
|
)*
|
|
}
|
|
}
|
|
i_slint_common::for_each_enums!(print_enums);
|
|
|
|
writeln!(enums_pub, "}}")?;
|
|
writeln!(enums_priv, "}}")?;
|
|
|
|
// Print the key codes constants
|
|
// This is not an enum, but fits well in that file
|
|
writeln!(
|
|
enums_pub,
|
|
r#"
|
|
/// This namespace contains constants for each special non-printable key.
|
|
///
|
|
/// Each constant can be converted to SharedString.
|
|
/// The constants are meant to be used with the slint::Window::dispatch_key_press_event() and
|
|
/// slint::Window::dispatch_key_release_event() functions.
|
|
///
|
|
/// Example:
|
|
/// ```
|
|
/// window.dispatch_key_press_event(slint::platform::key_codes::Tab);
|
|
/// ```
|
|
namespace slint::platform::key_codes {{
|
|
"#
|
|
)?;
|
|
macro_rules! print_key_codes {
|
|
($($char:literal # $name:ident # $($qt:ident)|* # $($winit:ident $(($_pos:ident))?)|* # $($_xkb:ident)|*;)*) => {
|
|
$(
|
|
writeln!(enums_pub, "/// A constant that represents the key code to be used in slint::Window::dispatch_key_press_event()")?;
|
|
writeln!(enums_pub, r#"constexpr std::u8string_view {} = u8"\u{:04x}";"#, stringify!($name), $char as u32)?;
|
|
)*
|
|
};
|
|
}
|
|
i_slint_common::for_each_special_keys!(print_key_codes);
|
|
writeln!(enums_pub, "}}")?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn builtin_structs(path: &Path) -> anyhow::Result<()> {
|
|
let mut structs_pub = BufWriter::new(
|
|
std::fs::File::create(path.join("slint_builtin_structs.h"))
|
|
.context("Error creating slint_builtin_structs.h file")?,
|
|
);
|
|
writeln!(structs_pub, "#pragma once")?;
|
|
writeln!(structs_pub, "// This file is auto-generated from {}", file!())?;
|
|
writeln!(structs_pub, "namespace slint {{")?;
|
|
|
|
let mut structs_priv = BufWriter::new(
|
|
std::fs::File::create(path.join("slint_builtin_structs_internal.h"))
|
|
.context("Error creating slint_builtin_structs_internal.h file")?,
|
|
);
|
|
writeln!(structs_priv, "#pragma once")?;
|
|
writeln!(structs_priv, "// This file is auto-generated from {}", file!())?;
|
|
writeln!(structs_priv, "#include \"slint_builtin_structs.h\"")?;
|
|
writeln!(structs_priv, "#include \"slint_enums_internal.h\"")?;
|
|
writeln!(structs_priv, "#include \"slint_point.h\"")?;
|
|
writeln!(structs_priv, "#include \"slint_image.h\"")?;
|
|
writeln!(structs_priv, "namespace slint::cbindgen_private {{")?;
|
|
writeln!(structs_priv, "enum class KeyEventType : uint8_t;")?;
|
|
macro_rules! struct_file {
|
|
(StandardListViewItem) => {{
|
|
writeln!(structs_priv, "using slint::StandardListViewItem;")?;
|
|
&mut structs_pub
|
|
}};
|
|
($_:ident) => {
|
|
&mut structs_priv
|
|
};
|
|
}
|
|
macro_rules! print_structs {
|
|
($(
|
|
$(#[doc = $struct_doc:literal])*
|
|
$(#[non_exhaustive])?
|
|
$(#[derive(Copy, Eq)])?
|
|
struct $Name:ident {
|
|
@name = $inner_name:literal
|
|
export {
|
|
$( $(#[doc = $pub_doc:literal])* $pub_field:ident : $pub_type:ty, )*
|
|
}
|
|
private {
|
|
$( $(#[doc = $pri_doc:literal])* $pri_field:ident : $pri_type:ty, )*
|
|
}
|
|
}
|
|
)*) => {
|
|
$(
|
|
let file = struct_file!($Name);
|
|
$(writeln!(file, "///{}", $struct_doc)?;)*
|
|
writeln!(file, "struct {} {{", stringify!($Name))?;
|
|
$(
|
|
$(writeln!(file, " ///{}", $pub_doc)?;)*
|
|
let pub_type = match stringify!($pub_type) {
|
|
"i32" => "int32_t",
|
|
"f32" | "Coord" => "float",
|
|
other => other,
|
|
};
|
|
writeln!(file, " {} {};", pub_type, stringify!($pub_field))?;
|
|
)*
|
|
$(
|
|
$(writeln!(file, " ///{}", $pri_doc)?;)*
|
|
let pri_type = stringify!($pri_type).replace(' ', "");
|
|
let pri_type = match pri_type.as_str() {
|
|
"usize" => "uintptr_t",
|
|
"crate::animations::Instant" => "uint64_t",
|
|
// This shouldn't be accessed by the C++ anyway, just need to have the same ABI in a struct
|
|
"Option<i32>" => "std::pair<int32_t, int32_t>",
|
|
"Option<core::ops::Range<i32>>" => "std::tuple<int32_t, int32_t, int32_t>",
|
|
other => other,
|
|
};
|
|
writeln!(file, " {} {};", pri_type, stringify!($pri_field))?;
|
|
)*
|
|
writeln!(file, " /// \\private")?;
|
|
writeln!(file, " {}", format!("friend bool operator==(const {name}&, const {name}&) = default;", name = stringify!($Name)))?;
|
|
writeln!(file, " /// \\private")?;
|
|
writeln!(file, " {}", format!("friend bool operator!=(const {name}&, const {name}&) = default;", name = stringify!($Name)))?;
|
|
writeln!(file, "}};")?;
|
|
)*
|
|
};
|
|
}
|
|
i_slint_common::for_each_builtin_structs!(print_structs);
|
|
writeln!(structs_priv, "}}")?;
|
|
writeln!(structs_pub, "}}")?;
|
|
Ok(())
|
|
}
|
|
|
|
fn ensure_cargo_rerun_for_crate(
|
|
crate_dir: &Path,
|
|
dependencies: &mut Vec<PathBuf>,
|
|
) -> anyhow::Result<()> {
|
|
dependencies.push(crate_dir.to_path_buf());
|
|
for entry in std::fs::read_dir(crate_dir)? {
|
|
let entry = entry?;
|
|
if entry.path().extension().is_some_and(|e| e == "rs") {
|
|
dependencies.push(entry.path());
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn default_config() -> cbindgen::Config {
|
|
let mut config = cbindgen::Config::default();
|
|
config.macro_expansion.bitflags = true;
|
|
config.pragma_once = true;
|
|
config.include_version = true;
|
|
config.namespaces = Some(vec!["slint".into(), "cbindgen_private".into()]);
|
|
config.line_length = 100;
|
|
config.tab_width = 4;
|
|
// Note: we might need to switch to C if we need to generate bindings for language that needs C headers
|
|
config.language = cbindgen::Language::Cxx;
|
|
config.cpp_compat = true;
|
|
config.documentation = true;
|
|
config.export = cbindgen::ExportConfig {
|
|
rename: [
|
|
("Callback".into(), "private_api::CallbackHelper".into()),
|
|
("VoidArg".into(), "void".into()),
|
|
("FocusReasonArg".into(), "FocusReason".into()),
|
|
("KeyEventArg".into(), "KeyEvent".into()),
|
|
("PointerEventArg".into(), "PointerEvent".into()),
|
|
("DropEventArg".into(), "DropEvent".into()),
|
|
("PointerScrollEventArg".into(), "PointerScrollEvent".into()),
|
|
("PointArg".into(), "slint::LogicalPosition".into()),
|
|
("FloatArg".into(), "float".into()),
|
|
("IntArg".into(), "int".into()),
|
|
("MenuEntryArg".into(), "MenuEntry".into()),
|
|
// Note: these types are not the same, but they are only used in callback return types that are only used in C++ (set and called)
|
|
// therefore it is ok to reinterpret_cast
|
|
("MenuEntryModel".into(), "std::shared_ptr<slint::Model<MenuEntry>>".into()),
|
|
("Coord".into(), "float".into()),
|
|
]
|
|
.iter()
|
|
.cloned()
|
|
.collect(),
|
|
..Default::default()
|
|
};
|
|
config.defines = [
|
|
("target_pointer_width = 64".into(), "SLINT_TARGET_64".into()),
|
|
("target_pointer_width = 32".into(), "SLINT_TARGET_32".into()),
|
|
// Disable any wasm guarded code in C++, too - so that there are no gaps in enums.
|
|
("target_arch = wasm32".into(), "SLINT_TARGET_WASM".into()),
|
|
("target_os = android".into(), "__ANDROID__".into()),
|
|
// Disable Rust WGPU specific API feature
|
|
("feature = unstable-wgpu-24".into(), "SLINT_DISABLED_CODE".into()),
|
|
]
|
|
.iter()
|
|
.cloned()
|
|
.collect();
|
|
config.structure.associated_constants_in_body = true;
|
|
config.constant.allow_constexpr = true;
|
|
config
|
|
}
|
|
|
|
fn gen_item_declarations(items: &[&str]) -> String {
|
|
format!(
|
|
r#"
|
|
namespace slint::private_api {{
|
|
#define SLINT_DECL_ITEM(ItemName) \
|
|
extern const cbindgen_private::ItemVTable ItemName##VTable; \
|
|
extern SLINT_DLL_IMPORT const cbindgen_private::ItemVTable* slint_get_##ItemName##VTable();
|
|
|
|
extern "C" {{
|
|
{}
|
|
}}
|
|
|
|
#undef SLINT_DECL_ITEM
|
|
}}
|
|
"#,
|
|
items
|
|
.iter()
|
|
.map(|item_name| format!("SLINT_DECL_ITEM({item_name});"))
|
|
.collect::<Vec<_>>()
|
|
.join("\n")
|
|
)
|
|
}
|
|
|
|
fn gen_corelib(
|
|
root_dir: &Path,
|
|
include_dir: &Path,
|
|
dependencies: &mut Vec<PathBuf>,
|
|
enabled_features: EnabledFeatures,
|
|
) -> anyhow::Result<()> {
|
|
let mut config = default_config();
|
|
|
|
let items = [
|
|
"Empty",
|
|
"Rectangle",
|
|
"BasicBorderRectangle",
|
|
"BorderRectangle",
|
|
"DragArea",
|
|
"DropArea",
|
|
"ImageItem",
|
|
"ClippedImage",
|
|
"TouchArea",
|
|
"FocusScope",
|
|
"SwipeGestureHandler",
|
|
"Flickable",
|
|
"SimpleText",
|
|
"ComplexText",
|
|
"Path",
|
|
"WindowItem",
|
|
"TextInput",
|
|
"Clip",
|
|
"BoxShadow",
|
|
"Rotate",
|
|
"Opacity",
|
|
"Layer",
|
|
"ContextMenu",
|
|
"MenuItem",
|
|
];
|
|
|
|
config.export.include = [
|
|
"Clipboard",
|
|
"ItemTreeVTable",
|
|
"Slice",
|
|
"WindowAdapterRcOpaque",
|
|
"PropertyAnimation",
|
|
"AnimationDirection",
|
|
"EasingCurve",
|
|
"TextHorizontalAlignment",
|
|
"TextVerticalAlignment",
|
|
"TextOverflow",
|
|
"TextWrap",
|
|
"ImageFit",
|
|
"FillRule",
|
|
"MouseCursor",
|
|
"InputType",
|
|
"StandardButtonKind",
|
|
"DialogButtonRole",
|
|
"FocusReason",
|
|
"PointerEventKind",
|
|
"PointerEventButton",
|
|
"PointerEvent",
|
|
"PointerScrollEvent",
|
|
"Rect",
|
|
"SortOrder",
|
|
"BitmapFont",
|
|
"PhysicalRegion",
|
|
]
|
|
.iter()
|
|
.chain(items.iter())
|
|
.map(|x| x.to_string())
|
|
.collect();
|
|
|
|
let mut private_exported_types: std::collections::HashSet<String> =
|
|
config.export.include.iter().cloned().collect();
|
|
|
|
// included in generated_public.h
|
|
let public_exported_types = [
|
|
"RenderingState",
|
|
"SetRenderingNotifierError",
|
|
"GraphicsAPI",
|
|
"CloseRequestResponse",
|
|
"StandardListViewItem",
|
|
"Rgb8Pixel",
|
|
"Rgba8Pixel",
|
|
];
|
|
|
|
config.export.exclude = [
|
|
"SharedString",
|
|
"SharedVector",
|
|
"ImageInner",
|
|
"ImageCacheKey",
|
|
"Image",
|
|
"Color",
|
|
"PathData",
|
|
"PathElement",
|
|
"Brush",
|
|
"slint_new_path_elements",
|
|
"slint_new_path_events",
|
|
"Property",
|
|
"Slice",
|
|
"Timer",
|
|
"PropertyHandleOpaque",
|
|
"Callback",
|
|
"slint_property_listener_scope_evaluate",
|
|
"slint_property_listener_scope_is_dirty",
|
|
"PropertyTrackerOpaque",
|
|
"CallbackOpaque",
|
|
"WindowAdapterRc",
|
|
"VoidArg",
|
|
"DropEventArg",
|
|
"FocusReasonArg",
|
|
"KeyEventArg",
|
|
"PointerEventArg",
|
|
"PointerScrollEventArg",
|
|
"PointArg",
|
|
"Point",
|
|
"MenuEntryModel",
|
|
"MenuEntryArg",
|
|
"Coord",
|
|
"LogicalRect",
|
|
"LogicalPoint",
|
|
"LogicalPosition",
|
|
"LogicalLength",
|
|
]
|
|
.iter()
|
|
.chain(public_exported_types.iter())
|
|
.map(|x| x.to_string())
|
|
.collect();
|
|
|
|
let mut crate_dir = root_dir.to_owned();
|
|
crate_dir.extend(["internal", "core"].iter());
|
|
|
|
ensure_cargo_rerun_for_crate(&crate_dir, dependencies)?;
|
|
|
|
let mut string_config = config.clone();
|
|
string_config.export.exclude = vec!["SharedString".into()];
|
|
string_config.export.body.insert(
|
|
"Slice".to_owned(),
|
|
" const T &operator[](int i) const { return ptr[i]; }
|
|
/// Note: this doesn't initialize Slice properly, but we need to keep the struct as compatible with C
|
|
constexpr Slice() = default;
|
|
/// Rust uses a NonNull, so even empty slices shouldn't use nullptr
|
|
constexpr Slice(const T *ptr, uintptr_t len) : ptr(ptr ? const_cast<T*>(ptr) : reinterpret_cast<T*>(sizeof(T))), len(len) {}
|
|
"
|
|
.to_owned(),
|
|
);
|
|
cbindgen::Builder::new()
|
|
.with_config(string_config)
|
|
.with_src(crate_dir.join("string.rs"))
|
|
.with_src(crate_dir.join("slice.rs"))
|
|
.with_after_include("namespace slint { struct SharedString; }")
|
|
.generate()
|
|
.context("Unable to generate bindings for slint_string_internal.h")?
|
|
.write_to_file(include_dir.join("slint_string_internal.h"));
|
|
|
|
cbindgen::Builder::new()
|
|
.with_config(config.clone())
|
|
.with_src(crate_dir.join("sharedvector.rs"))
|
|
.with_after_include("namespace slint { template<typename T> struct SharedVector; }")
|
|
.generate()
|
|
.context("Unable to generate bindings for slint_sharedvector_internal.h")?
|
|
.write_to_file(include_dir.join("slint_sharedvector_internal.h"));
|
|
|
|
let mut properties_config = config.clone();
|
|
properties_config.export.exclude.clear();
|
|
properties_config.structure.derive_eq = true;
|
|
properties_config.structure.derive_neq = true;
|
|
private_exported_types.extend(properties_config.export.include.iter().cloned());
|
|
cbindgen::Builder::new()
|
|
.with_config(properties_config)
|
|
.with_src(crate_dir.join("properties.rs"))
|
|
.with_src(crate_dir.join("properties/ffi.rs"))
|
|
.with_src(crate_dir.join("callbacks.rs"))
|
|
.with_after_include("namespace slint { class Color; class Brush; }")
|
|
.generate()
|
|
.context("Unable to generate bindings for slint_properties_internal.h")?
|
|
.write_to_file(include_dir.join("slint_properties_internal.h"));
|
|
|
|
// slint_timer_internal.h:
|
|
let timer_config = {
|
|
let mut tmp = config.clone();
|
|
tmp.export.include = [
|
|
"TimerMode",
|
|
"slint_timer_start",
|
|
"slint_timer_singleshot",
|
|
"slint_timer_destroy",
|
|
"slint_timer_stop",
|
|
"slint_timer_restart",
|
|
"slint_timer_running",
|
|
]
|
|
.iter()
|
|
.map(|s| s.to_string())
|
|
.collect();
|
|
tmp
|
|
};
|
|
config.export.exclude.extend(timer_config.export.include.iter().cloned());
|
|
cbindgen::Builder::new()
|
|
.with_config(timer_config)
|
|
.with_src(crate_dir.join("timers.rs"))
|
|
.generate()
|
|
.context("Unable to generate bindings for slint_timer_internal.h")?
|
|
.write_to_file(include_dir.join("slint_timer_internal.h"));
|
|
|
|
for (rust_types, internal_header, prelude) in [
|
|
(
|
|
vec![
|
|
"ImageInner",
|
|
"Image",
|
|
"ImageCacheKey",
|
|
"Size",
|
|
"slint_image_size",
|
|
"slint_image_path",
|
|
"slint_image_load_from_path",
|
|
"slint_image_load_from_embedded_data",
|
|
"slint_image_from_embedded_textures",
|
|
"slint_image_compare_equal",
|
|
"slint_image_set_nine_slice_edges",
|
|
"slint_image_to_rgb8",
|
|
"slint_image_to_rgba8",
|
|
"slint_image_to_rgba8_premultiplied",
|
|
"SharedPixelBuffer",
|
|
"SharedImageBuffer",
|
|
"StaticTextures",
|
|
"BorrowedOpenGLTextureOrigin"
|
|
],
|
|
"slint_image_internal.h",
|
|
"#include \"slint_color.h\"\nnamespace slint::cbindgen_private { struct ParsedSVG{}; struct HTMLImage{}; using namespace vtable; namespace types{ struct NineSliceImage{}; } }",
|
|
),
|
|
(
|
|
vec!["Color", "slint_color_brighter", "slint_color_darker",
|
|
"slint_color_transparentize",
|
|
"slint_color_mix",
|
|
"slint_color_with_alpha",
|
|
"slint_color_to_hsva",
|
|
"slint_color_from_hsva",],
|
|
"slint_color_internal.h",
|
|
"",
|
|
),
|
|
(
|
|
vec!["PathData", "PathElement", "slint_new_path_elements", "slint_new_path_events", "Point"],
|
|
"slint_pathdata_internal.h",
|
|
"#include \"slint_sharedvector.h\"\n#include \"slint_point.h\"",
|
|
),
|
|
(
|
|
vec!["Brush", "LinearGradient", "GradientStop", "RadialGradient"],
|
|
"slint_brush_internal.h",
|
|
"",
|
|
),
|
|
(
|
|
vec!["MouseEvent"],
|
|
"slint_events_internal.h",
|
|
"#include \"slint_point.h\"
|
|
namespace slint::cbindgen_private {
|
|
struct KeyEvent; struct PointerEvent;
|
|
struct Rect;
|
|
using LogicalRect = Rect;
|
|
using LogicalPoint = Point2D<float>;
|
|
using LogicalLength = float;
|
|
}",
|
|
)
|
|
]
|
|
.iter()
|
|
{
|
|
let mut special_config = config.clone();
|
|
special_config.export.include = rust_types.iter().map(|s| s.to_string()).collect();
|
|
special_config.export.exclude = [
|
|
"slint_visit_item_tree",
|
|
"slint_windowrc_drop",
|
|
"slint_windowrc_clone",
|
|
"slint_windowrc_show",
|
|
"slint_windowrc_hide",
|
|
"slint_windowrc_is_visible",
|
|
"slint_windowrc_get_scale_factor",
|
|
"slint_windowrc_set_scale_factor",
|
|
"slint_windowrc_get_text_input_focused",
|
|
"slint_windowrc_set_text_input_focused",
|
|
"slint_windowrc_set_focus_item",
|
|
"slint_windowrc_set_component",
|
|
"slint_windowrc_show_popup",
|
|
"slint_windowrc_close_popup",
|
|
"slint_windowrc_set_rendering_notifier",
|
|
"slint_windowrc_request_redraw",
|
|
"slint_windowrc_on_close_requested",
|
|
"slint_windowrc_position",
|
|
"slint_windowrc_set_logical_position",
|
|
"slint_windowrc_set_physical_position",
|
|
"slint_windowrc_size",
|
|
"slint_windowrc_set_logical_size",
|
|
"slint_windowrc_set_physical_size",
|
|
"slint_windowrc_color_scheme",
|
|
"slint_windowrc_supports_native_menu_bar",
|
|
"slint_windowrc_setup_native_menu_bar",
|
|
"slint_windowrc_default_font_size",
|
|
"slint_windowrc_dispatch_pointer_event",
|
|
"slint_windowrc_dispatch_key_event",
|
|
"slint_windowrc_dispatch_event",
|
|
"slint_windowrc_set_fullscreen",
|
|
"slint_windowrc_set_minimized",
|
|
"slint_windowrc_set_maximized",
|
|
"slint_windowrc_is_fullscreen",
|
|
"slint_windowrc_is_minimized",
|
|
"slint_windowrc_is_maximized",
|
|
"slint_windowrc_take_snapshot",
|
|
]
|
|
.into_iter()
|
|
.chain(config.export.exclude.iter().map(|s| s.as_str()))
|
|
.filter(|exclusion| !rust_types.iter().any(|inclusion| inclusion == exclusion))
|
|
.map(|s| s.to_string())
|
|
.collect();
|
|
|
|
config.export.exclude.extend(rust_types.iter().map(|s| s.to_string()));
|
|
|
|
special_config.enumeration = cbindgen::EnumConfig {
|
|
derive_tagged_enum_copy_assignment: true,
|
|
derive_tagged_enum_copy_constructor: true,
|
|
derive_tagged_enum_destructor: true,
|
|
derive_helper_methods: true,
|
|
private_default_tagged_enum_constructor: true,
|
|
..Default::default()
|
|
};
|
|
special_config.structure.derive_eq = true;
|
|
special_config.structure.derive_neq = true;
|
|
// Put the rust type in a deeper "types" namespace, so the use of same type in for example generated
|
|
// Property<> fields uses the public `slint::Blah` type
|
|
special_config.namespaces =
|
|
Some(vec!["slint".into(), "cbindgen_private".into(), "types".into()]);
|
|
|
|
private_exported_types.extend(special_config.export.include.iter().cloned());
|
|
|
|
special_config.after_includes = (!prelude.is_empty()).then(|| prelude.to_string());
|
|
|
|
cbindgen::Builder::new()
|
|
.with_config(special_config)
|
|
.with_src(crate_dir.join("graphics.rs"))
|
|
.with_src(crate_dir.join("graphics/color.rs"))
|
|
.with_src(crate_dir.join("graphics/path.rs"))
|
|
.with_src(crate_dir.join("graphics/brush.rs"))
|
|
.with_src(crate_dir.join("graphics/image.rs"))
|
|
.with_src(crate_dir.join("graphics/image/cache.rs"))
|
|
.with_src(crate_dir.join("animations.rs"))
|
|
.with_src(crate_dir.join("input.rs"))
|
|
.with_src(crate_dir.join("item_rendering.rs"))
|
|
.with_src(crate_dir.join("window.rs"))
|
|
.with_include("slint_enums_internal.h")
|
|
.generate()
|
|
.with_context(|| format!("Unable to generate bindings for {internal_header}"))?
|
|
.write_to_file(include_dir.join(internal_header));
|
|
}
|
|
|
|
// Generate a header file with some public API (enums, etc.)
|
|
let mut public_config = config.clone();
|
|
public_config.namespaces = Some(vec!["slint".into()]);
|
|
public_config.export.item_types = vec![cbindgen::ItemType::Enums, cbindgen::ItemType::Structs];
|
|
// Previously included types are now excluded (to avoid duplicates)
|
|
public_config.export.exclude = private_exported_types.into_iter().collect();
|
|
public_config.export.exclude.push("LogicalPosition".into());
|
|
public_config.export.exclude.push("MenuVTable".into());
|
|
public_config.export.include = public_exported_types.into_iter().map(str::to_string).collect();
|
|
public_config.export.body.insert(
|
|
"Rgb8Pixel".to_owned(),
|
|
"/// \\private\nfriend bool operator==(const Rgb8Pixel&, const Rgb8Pixel&) = default;"
|
|
.into(),
|
|
);
|
|
public_config.export.body.insert(
|
|
"Rgba8Pixel".to_owned(),
|
|
"/// \\private\nfriend bool operator==(const Rgba8Pixel&, const Rgba8Pixel&) = default;"
|
|
.into(),
|
|
);
|
|
|
|
cbindgen::Builder::new()
|
|
.with_config(public_config)
|
|
.with_src(crate_dir.join("graphics.rs"))
|
|
.with_src(crate_dir.join("window.rs"))
|
|
.with_src(crate_dir.join("api.rs"))
|
|
.with_src(crate_dir.join("model.rs"))
|
|
.with_src(crate_dir.join("graphics/image.rs"))
|
|
.with_include("slint_string.h")
|
|
.with_after_include(format!(
|
|
r#"
|
|
/// This macro expands to the to the numeric value of the major version of Slint you're
|
|
/// developing against. For example if you're using version 1.5.2, this macro will expand to 1.
|
|
#define SLINT_VERSION_MAJOR {x}
|
|
/// This macro expands to the to the numeric value of the minor version of Slint you're
|
|
/// developing against. For example if you're using version 1.5.2, this macro will expand to 5.
|
|
#define SLINT_VERSION_MINOR {y}
|
|
/// This macro expands to the to the numeric value of the patch version of Slint you're
|
|
/// developing against. For example if you're using version 1.5.2, this macro will expand to 2.
|
|
#define SLINT_VERSION_PATCH {z}
|
|
/// This macro expands to the string representation of the version of Slint you're developing against.
|
|
/// For example if you're using version 1.5.2, this macro will expand to "1.5.2".
|
|
#define SLINT_VERSION_STRING "{x}.{y}.{z}"
|
|
|
|
{features}
|
|
"#,
|
|
x = env!("CARGO_PKG_VERSION_MAJOR"),
|
|
y = env!("CARGO_PKG_VERSION_MINOR"),
|
|
z = env!("CARGO_PKG_VERSION_PATCH"),
|
|
features = enabled_features.defines()
|
|
))
|
|
.generate()
|
|
.context("Unable to generate bindings for slint_generated_public.h")?
|
|
.write_to_file(include_dir.join("slint_generated_public.h"));
|
|
|
|
config.export.body.insert(
|
|
"ItemTreeNode".to_owned(),
|
|
" constexpr ItemTreeNode(Item_Body x) : item {x} {}
|
|
constexpr ItemTreeNode(DynamicTree_Body x) : dynamic_tree{x} {}"
|
|
.to_owned(),
|
|
);
|
|
config.export.body.insert(
|
|
"CachedRenderingData".to_owned(),
|
|
" constexpr CachedRenderingData() : cache_index{}, cache_generation{} {}".to_owned(),
|
|
);
|
|
config.export.body.insert(
|
|
"EasingCurve".to_owned(),
|
|
" constexpr EasingCurve(EasingCurve::Tag tag = Tag::Linear, float a = 0, float b = 0, float c = 1, float d = 1) : tag(tag), cubic_bezier{{a,b,c,d}} {}".into()
|
|
);
|
|
config.export.body.insert(
|
|
"LayoutInfo".to_owned(),
|
|
" inline LayoutInfo merge(const LayoutInfo &other) const;
|
|
friend inline LayoutInfo operator+(const LayoutInfo &a, const LayoutInfo &b) { return a.merge(b); }
|
|
friend bool operator==(const LayoutInfo&, const LayoutInfo&) = default;".into(),
|
|
);
|
|
config.export.body.insert(
|
|
"WindowEvent".to_owned(),
|
|
"/* Some members of the WindowEvent enum have destructors (with SharedString), but thankfully we don't use these so we can have an empty constructor */
|
|
~WindowEvent() {}"
|
|
.into(),
|
|
);
|
|
config
|
|
.export
|
|
.body
|
|
.insert("Flickable".to_owned(), " inline Flickable(); inline ~Flickable();".into());
|
|
config.export.pre_body.insert("FlickableDataBox".to_owned(), "struct FlickableData;".into());
|
|
|
|
cbindgen::Builder::new()
|
|
.with_config(config)
|
|
.with_src(crate_dir.join("lib.rs"))
|
|
.with_include("slint_config.h")
|
|
.with_include("vtable.h")
|
|
.with_include("slint_string.h")
|
|
.with_include("slint_sharedvector.h")
|
|
.with_include("slint_properties.h")
|
|
.with_include("slint_callbacks.h")
|
|
.with_include("slint_color.h")
|
|
.with_include("slint_image.h")
|
|
.with_include("slint_pathdata.h")
|
|
.with_include("slint_brush.h")
|
|
.with_include("slint_generated_public.h")
|
|
.with_include("slint_enums_internal.h")
|
|
.with_include("slint_point.h")
|
|
.with_include("slint_timer.h")
|
|
.with_include("slint_builtin_structs_internal.h")
|
|
.with_include("slint_events_internal.h")
|
|
.with_after_include(
|
|
r"
|
|
namespace slint {
|
|
namespace private_api { class WindowAdapterRc; }
|
|
namespace cbindgen_private {
|
|
using slint::private_api::WindowAdapterRc;
|
|
using namespace vtable;
|
|
using private_api::Property;
|
|
using private_api::PathData;
|
|
using private_api::Point;
|
|
struct ItemTreeVTable;
|
|
struct ItemVTable;
|
|
using types::IntRect;
|
|
using types::Size;
|
|
using types::MouseEvent;
|
|
}
|
|
template<typename ModelData> class Model;
|
|
}",
|
|
)
|
|
.with_trailer(gen_item_declarations(&items))
|
|
.generate()
|
|
.expect("Unable to generate bindings")
|
|
.write_to_file(include_dir.join("slint_internal.h"));
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn gen_backend_qt(
|
|
root_dir: &Path,
|
|
include_dir: &Path,
|
|
dependencies: &mut Vec<PathBuf>,
|
|
) -> anyhow::Result<()> {
|
|
let mut config = default_config();
|
|
|
|
let items = [
|
|
"NativeButton",
|
|
"NativeSpinBox",
|
|
"NativeCheckBox",
|
|
"NativeSlider",
|
|
"NativeProgressIndicator",
|
|
"NativeGroupBox",
|
|
"NativeLineEdit",
|
|
"NativeScrollView",
|
|
"NativeStandardListViewItem",
|
|
"NativeTableHeaderSection",
|
|
"NativeComboBox",
|
|
"NativeComboBoxPopup",
|
|
"NativeTabWidget",
|
|
"NativeTab",
|
|
"NativeStyleMetrics",
|
|
"NativePalette",
|
|
];
|
|
|
|
config.export.include = items.iter().map(|x| x.to_string()).collect();
|
|
config.export.exclude = vec!["FloatArg".into(), "IntArg".into()];
|
|
|
|
config.export.body.insert(
|
|
"NativeStyleMetrics".to_owned(),
|
|
" inline explicit NativeStyleMetrics(void* = nullptr); inline ~NativeStyleMetrics();"
|
|
.to_owned(),
|
|
);
|
|
|
|
config.export.body.insert(
|
|
"NativePalette".to_owned(),
|
|
" inline explicit NativePalette(void* = nullptr); inline ~NativePalette();".to_owned(),
|
|
);
|
|
|
|
let mut crate_dir = root_dir.to_owned();
|
|
crate_dir.extend(["internal", "backends", "qt"].iter());
|
|
|
|
ensure_cargo_rerun_for_crate(&crate_dir, dependencies)?;
|
|
|
|
cbindgen::Builder::new()
|
|
.with_config(config)
|
|
.with_crate(crate_dir)
|
|
.with_include("slint_internal.h")
|
|
.with_after_include(
|
|
r"
|
|
namespace slint::cbindgen_private {
|
|
// HACK ALERT: This struct declaration is duplicated in internal/backend/qt/qt_widgets.rs - keep in sync.
|
|
struct SlintTypeErasedWidget
|
|
{
|
|
virtual ~SlintTypeErasedWidget() = 0;
|
|
SlintTypeErasedWidget(const SlintTypeErasedWidget&) = delete;
|
|
SlintTypeErasedWidget& operator=(const SlintTypeErasedWidget&) = delete;
|
|
|
|
virtual void *qwidget() const = 0;
|
|
};
|
|
using SlintTypeErasedWidgetPtr = std::unique_ptr<SlintTypeErasedWidget>;
|
|
}
|
|
",
|
|
)
|
|
.with_trailer(gen_item_declarations(&items))
|
|
.generate()
|
|
.context("Unable to generate bindings for slint_qt_internal.h")?
|
|
.write_to_file(include_dir.join("slint_qt_internal.h"));
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn gen_testing(
|
|
root_dir: &Path,
|
|
include_dir: &Path,
|
|
dependencies: &mut Vec<PathBuf>,
|
|
) -> anyhow::Result<()> {
|
|
let config = default_config();
|
|
|
|
let mut crate_dir = root_dir.to_owned();
|
|
crate_dir.extend(["internal", "backends", "testing"].iter());
|
|
|
|
ensure_cargo_rerun_for_crate(&crate_dir, dependencies)?;
|
|
|
|
cbindgen::Builder::new()
|
|
.with_config(config)
|
|
.with_crate(crate_dir)
|
|
.with_include("slint_testing_internal.h")
|
|
.generate()
|
|
.context("Unable to generate bindings for slint_testing_internal.h")?
|
|
.write_to_file(include_dir.join("slint_testing_internal.h"));
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn gen_platform(
|
|
root_dir: &Path,
|
|
include_dir: &Path,
|
|
dependencies: &mut Vec<PathBuf>,
|
|
) -> anyhow::Result<()> {
|
|
let config = default_config();
|
|
let mut crate_dir = root_dir.to_owned();
|
|
crate_dir.extend(["api", "cpp"].iter());
|
|
|
|
ensure_cargo_rerun_for_crate(&crate_dir, dependencies)?;
|
|
|
|
cbindgen::Builder::new()
|
|
.with_config(config)
|
|
.with_crate(crate_dir)
|
|
.with_include("slint_image_internal.h")
|
|
.with_include("slint_internal.h")
|
|
.with_after_include(
|
|
r"
|
|
namespace slint::platform { struct Rgb565Pixel; }
|
|
namespace slint::cbindgen_private {
|
|
struct WindowProperties; using slint::platform::Rgb565Pixel;
|
|
using slint::cbindgen_private::types::TexturePixelFormat;
|
|
struct DrawTextureArgs;
|
|
struct DrawRectangleArgs;
|
|
}
|
|
",
|
|
)
|
|
.generate()
|
|
.context("Unable to generate bindings for slint_platform_internal.h")?
|
|
.write_to_file(include_dir.join("slint_platform_internal.h"));
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn gen_interpreter(
|
|
root_dir: &Path,
|
|
include_dir: &Path,
|
|
dependencies: &mut Vec<PathBuf>,
|
|
) -> anyhow::Result<()> {
|
|
let mut config = default_config();
|
|
config.export.exclude = IntoIterator::into_iter([
|
|
"Value",
|
|
"ValueType",
|
|
"PropertyDescriptor",
|
|
"Diagnostic",
|
|
"PropertyDescriptor",
|
|
"Box",
|
|
])
|
|
.map(String::from)
|
|
.collect();
|
|
let mut crate_dir = root_dir.to_owned();
|
|
|
|
crate_dir.extend(["internal", "interpreter"].iter());
|
|
ensure_cargo_rerun_for_crate(&crate_dir, dependencies)?;
|
|
|
|
// Generate a header file with some public API (enums, etc.)
|
|
let mut public_config = config.clone();
|
|
public_config.namespaces = Some(vec!["slint".into(), "interpreter".into()]);
|
|
public_config.export.item_types = vec![cbindgen::ItemType::Enums, cbindgen::ItemType::Structs];
|
|
|
|
public_config.export.exclude = IntoIterator::into_iter([
|
|
"ComponentCompilerOpaque",
|
|
"ComponentDefinitionOpaque",
|
|
"ModelAdaptorVTable",
|
|
"StructIteratorOpaque",
|
|
"ComponentInstance",
|
|
"StructIteratorResult",
|
|
"Value",
|
|
"StructOpaque",
|
|
"ModelNotifyOpaque",
|
|
])
|
|
.map(String::from)
|
|
.collect();
|
|
|
|
cbindgen::Builder::new()
|
|
.with_config(public_config)
|
|
.with_crate(crate_dir.clone())
|
|
.generate()
|
|
.context("Unable to generate bindings for slint_interpreter_generated_public.h")?
|
|
.write_to_file(include_dir.join("slint_interpreter_generated_public.h"));
|
|
|
|
cbindgen::Builder::new()
|
|
.with_config(config)
|
|
.with_crate(crate_dir)
|
|
.with_include("slint_internal.h")
|
|
.with_include("slint_interpreter_generated_public.h")
|
|
.with_after_include(
|
|
r"
|
|
namespace slint::cbindgen_private {
|
|
struct Value;
|
|
using slint::interpreter::ValueType;
|
|
using slint::interpreter::PropertyDescriptor;
|
|
using slint::interpreter::Diagnostic;
|
|
template <typename T> using Box = T*;
|
|
}",
|
|
)
|
|
.generate()
|
|
.context("Unable to generate bindings for slint_interpreter_internal.h")?
|
|
.write_to_file(include_dir.join("slint_interpreter_internal.h"));
|
|
|
|
Ok(())
|
|
}
|
|
|
|
macro_rules! declare_features {
|
|
($($f:ident)+) => {
|
|
#[derive(Clone, Copy)]
|
|
pub struct EnabledFeatures {
|
|
$(pub $f: bool,)*
|
|
}
|
|
impl EnabledFeatures {
|
|
/// Generate the `#define`
|
|
pub fn defines(self) -> String {
|
|
let mut defines = String::new();
|
|
$(
|
|
if self.$f {
|
|
defines = format!("{defines}///This macro is defined when Slint was configured with the SLINT_FEATURE_{0} flag enabled\n#define SLINT_FEATURE_{0}\n", stringify!($f).to_ascii_uppercase());
|
|
};
|
|
)*
|
|
defines
|
|
}
|
|
|
|
/// Get the feature from the environment variable set by cargo when building running the slint-cpp's build script
|
|
#[allow(unused)]
|
|
pub fn from_env() -> Self {
|
|
Self {
|
|
$(
|
|
$f: std::env::var(format!("CARGO_FEATURE_{}", stringify!($f).to_ascii_uppercase())).is_ok(),
|
|
)*
|
|
}
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
declare_features! {
|
|
interpreter
|
|
testing
|
|
backend_qt
|
|
backend_winit
|
|
backend_winit_x11
|
|
backend_winit_wayland
|
|
backend_linuxkms
|
|
backend_linuxkms_noseat
|
|
renderer_femtovg
|
|
renderer_skia
|
|
renderer_skia_opengl
|
|
renderer_skia_vulkan
|
|
renderer_software
|
|
gettext
|
|
accessibility
|
|
system_testing
|
|
freestanding
|
|
experimental
|
|
}
|
|
|
|
/// Generate the headers.
|
|
/// `root_dir` is the root directory of the slint git repo
|
|
/// `include_dir` is the output directory
|
|
/// Returns the list of all paths that contain dependencies to the generated output. If you call this
|
|
/// function from build.rs, feed each entry to stdout prefixed with `cargo:rerun-if-changed=`.
|
|
pub fn gen_all(
|
|
root_dir: &Path,
|
|
include_dir: &Path,
|
|
enabled_features: EnabledFeatures,
|
|
) -> anyhow::Result<Vec<PathBuf>> {
|
|
proc_macro2::fallback::force(); // avoid a abort if panic=abort is set
|
|
std::fs::create_dir_all(include_dir).context("Could not create the include directory")?;
|
|
let mut deps = Vec::new();
|
|
enums(include_dir)?;
|
|
builtin_structs(include_dir)?;
|
|
gen_corelib(root_dir, include_dir, &mut deps, enabled_features)?;
|
|
gen_backend_qt(root_dir, include_dir, &mut deps)?;
|
|
gen_platform(root_dir, include_dir, &mut deps)?;
|
|
if enabled_features.testing {
|
|
gen_testing(root_dir, include_dir, &mut deps)?;
|
|
}
|
|
if enabled_features.interpreter {
|
|
gen_interpreter(root_dir, include_dir, &mut deps)?;
|
|
}
|
|
Ok(deps)
|
|
}
|