mirror of
https://github.com/slint-ui/slint.git
synced 2025-08-04 18:58:36 +00:00
Merge 595004226c
into 9a0390ea07
This commit is contained in:
commit
1a500fe862
12 changed files with 1071 additions and 40 deletions
|
@ -92,6 +92,7 @@ define_renderer_winit_compat_option(skia-vulkan)
|
|||
define_renderer_winit_compat_option(software)
|
||||
|
||||
define_cargo_dependent_feature(interpreter "Enable support for the Slint interpreter to load .slint files at run-time" ON "NOT SLINT_FEATURE_FREESTANDING")
|
||||
define_cargo_dependent_feature(live-reload "Enable support for the Slint live-reload to load .slint files at run-time" OFF "SLINT_FEATURE_INTERPRETER")
|
||||
|
||||
define_cargo_dependent_feature(backend-winit "Enable support for the winit crate to interaction with all windowing systems." ON "NOT SLINT_FEATURE_FREESTANDING")
|
||||
define_cargo_dependent_feature(backend-winit-x11 "Enable support for the winit create to interact only with the X11 windowing system on Unix. Enable this option and turn off SLINT_FEATURE_BACKEND_WINIT for a smaller build with just X11 support on Unix." OFF "NOT SLINT_FEATURE_FREESTANDING")
|
||||
|
|
|
@ -26,6 +26,7 @@ name = "slint_cpp"
|
|||
# the C++ crate's CMakeLists.txt as well as cbindgen.rs
|
||||
[features]
|
||||
interpreter = ["slint-interpreter", "std"]
|
||||
live-reload = ["interpreter", "slint-interpreter/internal-live-reload"]
|
||||
# Enable some function used by the integration tests
|
||||
testing = ["dep:i-slint-backend-testing"]
|
||||
|
||||
|
|
|
@ -896,6 +896,7 @@ fn gen_interpreter(
|
|||
"Diagnostic",
|
||||
"PropertyDescriptor",
|
||||
"Box",
|
||||
"LiveReloadingComponentInner",
|
||||
])
|
||||
.map(String::from)
|
||||
.collect();
|
||||
|
@ -942,6 +943,7 @@ fn gen_interpreter(
|
|||
using slint::interpreter::ValueType;
|
||||
using slint::interpreter::PropertyDescriptor;
|
||||
using slint::interpreter::Diagnostic;
|
||||
struct LiveReloadingComponentInner;
|
||||
template <typename T> using Box = T*;
|
||||
}",
|
||||
)
|
||||
|
|
|
@ -26,6 +26,10 @@ struct ErasedItemTreeBox : vtable::Dyn
|
|||
ErasedItemTreeBox(ErasedItemTreeBox &) = delete;
|
||||
};
|
||||
}
|
||||
namespace slint::private_api::live_reload {
|
||||
class LiveReloadingComponent;
|
||||
class LiveReloadModelWrapperBase;
|
||||
}
|
||||
|
||||
/// The types in this namespace allow you to load a .slint file at runtime and show its UI.
|
||||
///
|
||||
|
@ -396,6 +400,8 @@ private:
|
|||
slint::cbindgen_private::Value *inner;
|
||||
friend struct Struct;
|
||||
friend class ComponentInstance;
|
||||
friend class slint::private_api::live_reload::LiveReloadingComponent;
|
||||
friend class slint::private_api::live_reload::LiveReloadModelWrapperBase;
|
||||
// Internal constructor that takes ownership of the value
|
||||
explicit Value(slint::cbindgen_private::Value *&&inner) : inner(inner) { }
|
||||
};
|
||||
|
|
349
api/cpp/include/slint_live_reload.h
Normal file
349
api/cpp/include/slint_live_reload.h
Normal file
|
@ -0,0 +1,349 @@
|
|||
// 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
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "slint.h"
|
||||
|
||||
#define SLINT_FEATURE_LIVE_RELOAD
|
||||
|
||||
#ifndef SLINT_FEATURE_LIVE_RELOAD
|
||||
# error SLINT_FEATURE_LIVE_RELOAD must be activated
|
||||
#else
|
||||
|
||||
# include "slint-interpreter.h"
|
||||
|
||||
/// Internal API to support the live-reload generated code
|
||||
namespace slint::private_api::live_reload {
|
||||
|
||||
template<typename T>
|
||||
requires(std::convertible_to<T, slint::interpreter::Value>)
|
||||
slint::interpreter::Value into_slint_value(const T &val)
|
||||
{
|
||||
return val;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
requires requires(T val) { val.into_slint_value(); }
|
||||
slint::interpreter::Value into_slint_value(const T &val)
|
||||
{
|
||||
return val.into_slint_value();
|
||||
}
|
||||
|
||||
inline slint::interpreter::Value into_slint_value(const slint::interpreter::Value &val)
|
||||
{
|
||||
return val;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
requires std::is_same_v<T, void>
|
||||
inline void from_slint_value(const slint::interpreter::Value &, const T *)
|
||||
{
|
||||
}
|
||||
inline bool from_slint_value(const slint::interpreter::Value &val, const bool *)
|
||||
{
|
||||
return val.to_bool().value();
|
||||
}
|
||||
inline slint::SharedString from_slint_value(const slint::interpreter::Value &val,
|
||||
const slint::SharedString *)
|
||||
{
|
||||
return val.to_string().value();
|
||||
}
|
||||
inline int from_slint_value(const slint::interpreter::Value &val, const int *)
|
||||
{
|
||||
return val.to_number().value();
|
||||
}
|
||||
inline float from_slint_value(const slint::interpreter::Value &val, const float *)
|
||||
{
|
||||
return val.to_number().value();
|
||||
}
|
||||
inline slint::Color from_slint_value(const slint::interpreter::Value &val, const slint::Color *)
|
||||
{
|
||||
return val.to_brush().value().color();
|
||||
}
|
||||
inline interpreter::Value into_slint_value(const slint::Color &val)
|
||||
{
|
||||
return slint::Brush(val);
|
||||
}
|
||||
inline slint::Brush from_slint_value(const slint::interpreter::Value &val, const slint::Brush *)
|
||||
{
|
||||
return val.to_brush().value();
|
||||
}
|
||||
inline slint::Image from_slint_value(const slint::interpreter::Value &val, const slint::Image *)
|
||||
{
|
||||
return val.to_image().value();
|
||||
}
|
||||
/// duration
|
||||
inline long int from_slint_value(const slint::interpreter::Value &val, const long int *)
|
||||
{
|
||||
return val.to_number().value();
|
||||
}
|
||||
inline interpreter::Value into_slint_value(const long int &val)
|
||||
{
|
||||
return double(val);
|
||||
}
|
||||
|
||||
template<typename ModelData>
|
||||
inline std::shared_ptr<slint::Model<ModelData>>
|
||||
from_slint_value(const slint::interpreter::Value &,
|
||||
const std::shared_ptr<slint::Model<ModelData>> *)
|
||||
{
|
||||
std::cout << "NOT IMPLEMENTED " << __PRETTY_FUNCTION__ << std::endl;
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename ModelData>
|
||||
slint::interpreter::Value into_slint_value(const std::shared_ptr<slint::Model<ModelData>> &val);
|
||||
|
||||
inline slint::interpreter::Value into_slint_value(const slint::StandardListViewItem &val)
|
||||
{
|
||||
slint::interpreter::Struct s;
|
||||
s.set_field("text", val.text);
|
||||
return s;
|
||||
}
|
||||
|
||||
inline slint::StandardListViewItem from_slint_value(const slint::interpreter::Value &val,
|
||||
const slint::StandardListViewItem *)
|
||||
{
|
||||
auto s = val.to_struct().value();
|
||||
return slint::StandardListViewItem { .text = s.get_field("text").value().to_string().value() };
|
||||
}
|
||||
|
||||
inline slint::interpreter::Value into_slint_value(const slint::LogicalPosition &val)
|
||||
{
|
||||
slint::interpreter::Struct s;
|
||||
s.set_field("x", val.x);
|
||||
s.set_field("y", val.y);
|
||||
return s;
|
||||
}
|
||||
|
||||
inline slint::LogicalPosition from_slint_value(const slint::interpreter::Value &val,
|
||||
const slint::LogicalPosition *)
|
||||
{
|
||||
auto s = val.to_struct().value();
|
||||
return slint::LogicalPosition({ float(s.get_field("x").value().to_number().value()),
|
||||
float(s.get_field("y").value().to_number().value()) });
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T from_slint_value(const slint::interpreter::Value &v)
|
||||
{
|
||||
return from_slint_value(v, static_cast<const T *>(nullptr));
|
||||
}
|
||||
|
||||
class LiveReloadingComponent
|
||||
{
|
||||
const cbindgen_private::LiveReloadingComponentInner *inner;
|
||||
|
||||
public:
|
||||
/// Libraries is an array of string that have in the form `lib=...`
|
||||
LiveReloadingComponent(std::string_view file_name, std::string_view component_name,
|
||||
const slint::SharedVector<slint::SharedString> &include_paths,
|
||||
const slint::SharedVector<slint::SharedString> &libraries,
|
||||
std::string_view style)
|
||||
{
|
||||
assert_main_thread();
|
||||
inner = cbindgen_private::slint_live_reload_new(
|
||||
string_to_slice(file_name), string_to_slice(component_name), &include_paths,
|
||||
&libraries, string_to_slice(style));
|
||||
}
|
||||
|
||||
LiveReloadingComponent(const LiveReloadingComponent &other) : inner(other.inner)
|
||||
{
|
||||
assert_main_thread();
|
||||
cbindgen_private::slint_live_reload_clone(other.inner);
|
||||
}
|
||||
LiveReloadingComponent &operator=(const LiveReloadingComponent &other)
|
||||
{
|
||||
assert_main_thread();
|
||||
if (this == &other)
|
||||
return *this;
|
||||
cbindgen_private::slint_live_reload_drop(inner);
|
||||
inner = other.inner;
|
||||
cbindgen_private::slint_live_reload_clone(inner);
|
||||
return *this;
|
||||
}
|
||||
~LiveReloadingComponent()
|
||||
{
|
||||
assert_main_thread();
|
||||
cbindgen_private::slint_live_reload_drop(inner);
|
||||
}
|
||||
|
||||
void set_property(std::string_view name, const interpreter::Value &value) const
|
||||
{
|
||||
assert_main_thread();
|
||||
return cbindgen_private::slint_live_reload_set_property(inner, string_to_slice(name),
|
||||
value.inner);
|
||||
}
|
||||
|
||||
interpreter::Value get_property(std::string_view name) const
|
||||
{
|
||||
assert_main_thread();
|
||||
auto val = slint::interpreter::Value(
|
||||
cbindgen_private::slint_live_reload_get_property(inner, string_to_slice(name)));
|
||||
return val;
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
interpreter::Value invoke(std::string_view name, Args &...args) const
|
||||
{
|
||||
assert_main_thread();
|
||||
std::array<interpreter::Value, sizeof...(Args)> args_values { into_slint_value(args)... };
|
||||
cbindgen_private::Slice<cbindgen_private::Value *> args_slice {
|
||||
reinterpret_cast<cbindgen_private::Value **>(args_values.data()), args_values.size()
|
||||
};
|
||||
interpreter::Value val(cbindgen_private::slint_live_reload_invoke(
|
||||
inner, string_to_slice(name), args_slice));
|
||||
return val;
|
||||
}
|
||||
|
||||
template<std::invocable<std::span<const interpreter::Value>> F>
|
||||
requires(std::is_convertible_v<std::invoke_result_t<F, std::span<const interpreter::Value>>,
|
||||
interpreter::Value>)
|
||||
void set_callback(std::string_view name, F &&callback) const
|
||||
{
|
||||
assert_main_thread();
|
||||
auto actual_cb =
|
||||
[](void *data,
|
||||
cbindgen_private::Slice<cbindgen_private::Box<cbindgen_private::Value>> arg) {
|
||||
std::span<const interpreter::Value> args_view {
|
||||
reinterpret_cast<const interpreter::Value *>(arg.ptr), arg.len
|
||||
};
|
||||
interpreter::Value r = (*reinterpret_cast<F *>(data))(args_view);
|
||||
auto inner = r.inner;
|
||||
r.inner = cbindgen_private::slint_interpreter_value_new();
|
||||
return inner;
|
||||
};
|
||||
return cbindgen_private::slint_live_reload_set_callback(
|
||||
inner, slint::private_api::string_to_slice(name), actual_cb,
|
||||
new F(std::move(callback)), [](void *data) { delete reinterpret_cast<F *>(data); });
|
||||
}
|
||||
|
||||
slint::Window &window() const
|
||||
{
|
||||
const cbindgen_private::WindowAdapterRcOpaque *win_ptr = nullptr;
|
||||
cbindgen_private::slint_live_reload_window(inner, &win_ptr);
|
||||
return const_cast<slint::Window &>(*reinterpret_cast<const slint::Window *>(win_ptr));
|
||||
}
|
||||
};
|
||||
|
||||
class LiveReloadModelWrapperBase : public private_api::ModelChangeListener
|
||||
{
|
||||
cbindgen_private::ModelNotifyOpaque notify;
|
||||
// This means that the rust code has ownership of "this" until the drop function is called
|
||||
std::shared_ptr<ModelChangeListener> self = nullptr;
|
||||
|
||||
void row_added(size_t index, size_t count) override
|
||||
{
|
||||
cbindgen_private::slint_interpreter_model_notify_row_added(¬ify, index, count);
|
||||
}
|
||||
void row_changed(size_t index) override
|
||||
{
|
||||
cbindgen_private::slint_interpreter_model_notify_row_changed(¬ify, index);
|
||||
}
|
||||
void row_removed(size_t index, size_t count) override
|
||||
{
|
||||
cbindgen_private::slint_interpreter_model_notify_row_removed(¬ify, index, count);
|
||||
}
|
||||
void reset() override { cbindgen_private::slint_interpreter_model_notify_reset(¬ify); }
|
||||
|
||||
static const ModelAdaptorVTable *vtable()
|
||||
{
|
||||
auto row_count = [](VRef<ModelAdaptorVTable> self) -> uintptr_t {
|
||||
return reinterpret_cast<LiveReloadModelWrapperBase *>(self.instance)->row_count();
|
||||
};
|
||||
auto row_data = [](VRef<ModelAdaptorVTable> self,
|
||||
uintptr_t row) -> slint::cbindgen_private::Value * {
|
||||
std::optional<interpreter::Value> v =
|
||||
reinterpret_cast<LiveReloadModelWrapperBase *>(self.instance)
|
||||
->row_data(int(row));
|
||||
if (v.has_value()) {
|
||||
slint::cbindgen_private::Value *rval = v->inner;
|
||||
v->inner = cbindgen_private::slint_interpreter_value_new();
|
||||
return rval;
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
auto set_row_data = [](VRef<ModelAdaptorVTable> self, uintptr_t row,
|
||||
slint::cbindgen_private::Value *value) {
|
||||
interpreter::Value v(std::move(value));
|
||||
reinterpret_cast<LiveReloadModelWrapperBase *>(self.instance)->set_row_data(row, v);
|
||||
};
|
||||
auto get_notify =
|
||||
[](VRef<ModelAdaptorVTable> self) -> const cbindgen_private::ModelNotifyOpaque * {
|
||||
return &reinterpret_cast<LiveReloadModelWrapperBase *>(self.instance)->notify;
|
||||
};
|
||||
auto drop = [](vtable::VRefMut<ModelAdaptorVTable> self) {
|
||||
reinterpret_cast<LiveReloadModelWrapperBase *>(self.instance)->self = nullptr;
|
||||
};
|
||||
|
||||
static const ModelAdaptorVTable vt { row_count, row_data, set_row_data, get_notify, drop };
|
||||
return &vt;
|
||||
}
|
||||
|
||||
protected:
|
||||
LiveReloadModelWrapperBase() { cbindgen_private::slint_interpreter_model_notify_new(¬ify); }
|
||||
virtual ~LiveReloadModelWrapperBase()
|
||||
{
|
||||
cbindgen_private::slint_interpreter_model_notify_destructor(¬ify);
|
||||
}
|
||||
|
||||
virtual int row_count() const = 0;
|
||||
virtual std::optional<slint::interpreter::Value> row_data(int i) const = 0;
|
||||
virtual void set_row_data(int i, const slint::interpreter::Value &value) = 0;
|
||||
|
||||
static interpreter::Value wrap(std::shared_ptr<LiveReloadModelWrapperBase> wrapper)
|
||||
{
|
||||
wrapper->self = wrapper;
|
||||
return interpreter::Value(cbindgen_private::slint_interpreter_value_new_model(
|
||||
reinterpret_cast<uint8_t *>(wrapper.get()), vtable()));
|
||||
}
|
||||
};
|
||||
|
||||
template<typename ModelData>
|
||||
class LiveReloadModelWrapper : public LiveReloadModelWrapperBase
|
||||
{
|
||||
std::shared_ptr<slint::Model<ModelData>> model = nullptr;
|
||||
|
||||
int row_count() const override { return model->row_count(); }
|
||||
|
||||
std::optional<slint::interpreter::Value> row_data(int i) const override
|
||||
{
|
||||
if (auto v = model->row_data(i))
|
||||
return into_slint_value(*v);
|
||||
else
|
||||
return {};
|
||||
}
|
||||
|
||||
void set_row_data(int i, const slint::interpreter::Value &value) override
|
||||
{
|
||||
model->set_row_data(i, from_slint_value<ModelData>(value));
|
||||
}
|
||||
|
||||
public:
|
||||
LiveReloadModelWrapper(std::shared_ptr<slint::Model<ModelData>> model) : model(std::move(model))
|
||||
{
|
||||
}
|
||||
|
||||
static slint::interpreter::Value wrap(std::shared_ptr<slint::Model<ModelData>> model)
|
||||
{
|
||||
auto self = std::make_shared<LiveReloadModelWrapper<ModelData>>(model);
|
||||
auto peer = std::weak_ptr<LiveReloadModelWrapperBase>(self);
|
||||
model->attach_peer(peer);
|
||||
return LiveReloadModelWrapperBase::wrap(self);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename ModelData>
|
||||
slint::interpreter::Value into_slint_value(const std::shared_ptr<slint::Model<ModelData>> &val)
|
||||
{
|
||||
if (!val) {
|
||||
return {};
|
||||
}
|
||||
return LiveReloadModelWrapper<ModelData>::wrap(val);
|
||||
}
|
||||
|
||||
} // namespace slint::private_api::live_reload
|
||||
|
||||
#endif // SLINT_FEATURE_LIVE_RELOAD
|
|
@ -101,6 +101,7 @@ public:
|
|||
{
|
||||
if (!--inner->strong_ref) {
|
||||
Layout layout = inner->vtable->drop_in_place({ inner->vtable, &inner->data });
|
||||
layout.size = std::max<size_t>(layout.size, sizeof(Layout)); // because of the union
|
||||
layout.size += inner->data_offset;
|
||||
layout.align = std::max<size_t>(layout.align, alignof(VRcInner<VTable, Dyn>));
|
||||
inner->layout = layout;
|
||||
|
|
|
@ -21,7 +21,8 @@ use crate::CompilerConfiguration;
|
|||
|
||||
#[cfg(feature = "cpp")]
|
||||
pub mod cpp;
|
||||
|
||||
#[cfg(feature = "cpp")]
|
||||
pub mod cpp_live_reload;
|
||||
#[cfg(feature = "rust")]
|
||||
pub mod rust;
|
||||
#[cfg(feature = "rust")]
|
||||
|
|
|
@ -47,7 +47,7 @@ fn is_cpp_keyword(word: &str) -> bool {
|
|||
keywords.contains(word)
|
||||
}
|
||||
|
||||
fn ident(ident: &str) -> SmolStr {
|
||||
pub fn ident(ident: &str) -> SmolStr {
|
||||
let mut new_ident = SmolStr::from(ident);
|
||||
if ident.contains('-') {
|
||||
new_ident = ident.replace_smolstr("-", "_");
|
||||
|
@ -58,7 +58,7 @@ fn ident(ident: &str) -> SmolStr {
|
|||
new_ident
|
||||
}
|
||||
|
||||
fn concatenate_ident(ident: &str) -> SmolStr {
|
||||
pub fn concatenate_ident(ident: &str) -> SmolStr {
|
||||
if ident.contains('-') {
|
||||
ident.replace_smolstr("-", "_")
|
||||
} else {
|
||||
|
@ -109,7 +109,7 @@ fn access_item_rc(pr: &llr::PropertyReference, ctx: &EvaluationContext) -> Strin
|
|||
|
||||
/// This module contains some data structure that helps represent a C++ code.
|
||||
/// It is then rendered into an actual C++ text using the Display trait
|
||||
mod cpp_ast {
|
||||
pub mod cpp_ast {
|
||||
|
||||
use std::cell::Cell;
|
||||
use std::fmt::{Display, Error, Formatter};
|
||||
|
@ -681,33 +681,16 @@ pub fn generate(
|
|||
config: Config,
|
||||
compiler_config: &CompilerConfiguration,
|
||||
) -> std::io::Result<impl std::fmt::Display> {
|
||||
let mut file = File { namespace: config.namespace.clone(), ..Default::default() };
|
||||
if std::env::var("SLINT_LIVE_RELOAD").is_ok() {
|
||||
return super::cpp_live_reload::generate(doc, config, compiler_config);
|
||||
}
|
||||
|
||||
file.includes.push("<array>".into());
|
||||
file.includes.push("<limits>".into());
|
||||
file.includes.push("<slint.h>".into());
|
||||
let mut file = generate_types(&doc.used_types.borrow().structs_and_enums, &config);
|
||||
|
||||
for (path, er) in doc.embedded_file_resources.borrow().iter() {
|
||||
embed_resource(er, path, &mut file.resources);
|
||||
}
|
||||
|
||||
for ty in doc.used_types.borrow().structs_and_enums.iter() {
|
||||
match ty {
|
||||
Type::Struct(s) if s.name.is_some() && s.node.is_some() => {
|
||||
generate_struct(
|
||||
&mut file,
|
||||
s.name.as_ref().unwrap(),
|
||||
&s.fields,
|
||||
s.node.as_ref().unwrap(),
|
||||
);
|
||||
}
|
||||
Type::Enumeration(en) => {
|
||||
generate_enum(&mut file, en);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
let llr = llr::lower_to_item_tree::lower_to_item_tree(doc, compiler_config)?;
|
||||
|
||||
#[cfg(feature = "bundle-translations")]
|
||||
|
@ -874,15 +857,6 @@ pub fn generate(
|
|||
|
||||
generate_type_aliases(&mut file, doc);
|
||||
|
||||
file.after_includes = format!(
|
||||
"static_assert({x} == SLINT_VERSION_MAJOR && {y} == SLINT_VERSION_MINOR && {z} == SLINT_VERSION_PATCH, \
|
||||
\"This file was generated with Slint compiler version {x}.{y}.{z}, but the Slint library used is \" \
|
||||
SLINT_VERSION_STRING \". The version numbers must match exactly.\");",
|
||||
x = env!("CARGO_PKG_VERSION_MAJOR"),
|
||||
y = env!("CARGO_PKG_VERSION_MINOR"),
|
||||
z = env!("CARGO_PKG_VERSION_PATCH")
|
||||
);
|
||||
|
||||
if conditional_includes.iostream.get() {
|
||||
file.includes.push("<iostream>".into());
|
||||
}
|
||||
|
@ -905,6 +879,42 @@ pub fn generate(
|
|||
Ok(file)
|
||||
}
|
||||
|
||||
pub fn generate_types(used_types: &[Type], config: &Config) -> File {
|
||||
let mut file = File { namespace: config.namespace.clone(), ..Default::default() };
|
||||
|
||||
file.includes.push("<array>".into());
|
||||
file.includes.push("<limits>".into());
|
||||
file.includes.push("<slint.h>".into());
|
||||
|
||||
file.after_includes = format!(
|
||||
"static_assert({x} == SLINT_VERSION_MAJOR && {y} == SLINT_VERSION_MINOR && {z} == SLINT_VERSION_PATCH, \
|
||||
\"This file was generated with Slint compiler version {x}.{y}.{z}, but the Slint library used is \" \
|
||||
SLINT_VERSION_STRING \". The version numbers must match exactly.\");",
|
||||
x = env!("CARGO_PKG_VERSION_MAJOR"),
|
||||
y = env!("CARGO_PKG_VERSION_MINOR"),
|
||||
z = env!("CARGO_PKG_VERSION_PATCH")
|
||||
);
|
||||
|
||||
for ty in used_types {
|
||||
match ty {
|
||||
Type::Struct(s) if s.name.is_some() && s.node.is_some() => {
|
||||
generate_struct(
|
||||
&mut file,
|
||||
s.name.as_ref().unwrap(),
|
||||
&s.fields,
|
||||
s.node.as_ref().unwrap(),
|
||||
);
|
||||
}
|
||||
Type::Enumeration(en) => {
|
||||
generate_enum(&mut file, en);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
file
|
||||
}
|
||||
|
||||
fn embed_resource(
|
||||
resource: &crate::embedded_resources::EmbeddedResources,
|
||||
path: &SmolStr,
|
||||
|
@ -4089,7 +4099,7 @@ fn return_compile_expression(
|
|||
}
|
||||
}
|
||||
|
||||
fn generate_type_aliases(file: &mut File, doc: &Document) {
|
||||
pub fn generate_type_aliases(file: &mut File, doc: &Document) {
|
||||
let type_aliases = doc
|
||||
.exports
|
||||
.iter()
|
||||
|
|
518
internal/compiler/generator/cpp_live_reload.rs
Normal file
518
internal/compiler/generator/cpp_live_reload.rs
Normal file
|
@ -0,0 +1,518 @@
|
|||
// 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 super::cpp::{concatenate_ident, cpp_ast::*, ident, Config};
|
||||
use crate::langtype::Type;
|
||||
use crate::llr;
|
||||
use crate::object_tree::Document;
|
||||
use crate::CompilerConfiguration;
|
||||
use itertools::Itertools as _;
|
||||
use smol_str::format_smolstr;
|
||||
use std::io::BufWriter;
|
||||
|
||||
pub fn generate(
|
||||
doc: &Document,
|
||||
config: Config,
|
||||
compiler_config: &CompilerConfiguration,
|
||||
) -> std::io::Result<File> {
|
||||
let mut file = super::cpp::generate_types(&doc.used_types.borrow().structs_and_enums, &config);
|
||||
|
||||
file.includes.push("<slint_live_reload.h>".into());
|
||||
|
||||
generate_value_conversions(&mut file, &doc.used_types.borrow().structs_and_enums);
|
||||
|
||||
let llr = crate::llr::lower_to_item_tree::lower_to_item_tree(doc, compiler_config)?;
|
||||
|
||||
let main_file = doc
|
||||
.node
|
||||
.as_ref()
|
||||
.ok_or_else(|| std::io::Error::other("Cannot determine path of the main file"))?
|
||||
.source_file
|
||||
.path()
|
||||
.to_string_lossy();
|
||||
|
||||
for p in &llr.public_components {
|
||||
generate_public_component(&mut file, p, &llr, compiler_config, &main_file);
|
||||
}
|
||||
|
||||
for glob in &llr.globals {
|
||||
if glob.must_generate() {
|
||||
generate_global(&mut file, glob);
|
||||
file.definitions.extend(glob.aliases.iter().map(|name| {
|
||||
Declaration::TypeAlias(TypeAlias {
|
||||
old_name: ident(&glob.name),
|
||||
new_name: ident(name),
|
||||
})
|
||||
}));
|
||||
};
|
||||
}
|
||||
|
||||
super::cpp::generate_type_aliases(&mut file, doc);
|
||||
|
||||
let cpp_files = file.split_off_cpp_files(config.header_include, config.cpp_files.len());
|
||||
for (cpp_file_name, cpp_file) in config.cpp_files.iter().zip(cpp_files) {
|
||||
use std::io::Write;
|
||||
write!(&mut BufWriter::new(std::fs::File::create(&cpp_file_name)?), "{cpp_file}")?;
|
||||
}
|
||||
|
||||
Ok(file)
|
||||
}
|
||||
|
||||
fn generate_public_component(
|
||||
file: &mut File,
|
||||
component: &llr::PublicComponent,
|
||||
unit: &llr::CompilationUnit,
|
||||
compiler_config: &CompilerConfiguration,
|
||||
main_file: &str,
|
||||
) {
|
||||
let component_id = ident(&component.name);
|
||||
|
||||
let mut component_struct = Struct { name: component_id.clone(), ..Default::default() };
|
||||
|
||||
component_struct.members.push((
|
||||
Access::Private,
|
||||
Declaration::Var(Var {
|
||||
ty: "slint::private_api::live_reload::LiveReloadingComponent".into(),
|
||||
name: "live_reload".into(),
|
||||
..Default::default()
|
||||
}),
|
||||
));
|
||||
|
||||
let mut global_accessor_function_body = Vec::new();
|
||||
for glob in unit.globals.iter().filter(|glob| glob.exported && glob.must_generate()) {
|
||||
let accessor_statement = format!(
|
||||
"{0}if constexpr(std::is_same_v<T, {1}>) {{ return T(live_reload); }}",
|
||||
if global_accessor_function_body.is_empty() { "" } else { "else " },
|
||||
concatenate_ident(&glob.name),
|
||||
);
|
||||
global_accessor_function_body.push(accessor_statement);
|
||||
}
|
||||
if !global_accessor_function_body.is_empty() {
|
||||
global_accessor_function_body.push(
|
||||
"else { static_assert(!sizeof(T*), \"The type is not global/or exported\"); }".into(),
|
||||
);
|
||||
|
||||
component_struct.members.push((
|
||||
Access::Public,
|
||||
Declaration::Function(Function {
|
||||
name: "global".into(),
|
||||
signature: "() const -> T".into(),
|
||||
statements: Some(global_accessor_function_body),
|
||||
template_parameters: Some("typename T".into()),
|
||||
..Default::default()
|
||||
}),
|
||||
));
|
||||
}
|
||||
|
||||
generate_public_api_for_properties(
|
||||
"",
|
||||
&mut component_struct.members,
|
||||
&component.public_properties,
|
||||
&component.private_properties,
|
||||
);
|
||||
|
||||
component_struct.members.push((
|
||||
Access::Public,
|
||||
Declaration::Var(Var {
|
||||
ty: "static const slint::private_api::ItemTreeVTable".into(),
|
||||
name: "static_vtable".into(),
|
||||
..Default::default()
|
||||
}),
|
||||
));
|
||||
|
||||
file.definitions.push(Declaration::Var(Var {
|
||||
ty: "const slint::private_api::ItemTreeVTable".into(),
|
||||
name: format_smolstr!("{component_id}::static_vtable"),
|
||||
init: Some(format!(
|
||||
"{{ nullptr, nullptr, nullptr, nullptr, \
|
||||
nullptr, nullptr, nullptr, nullptr, nullptr, \
|
||||
nullptr, nullptr, nullptr, nullptr, \
|
||||
nullptr, nullptr, nullptr, \
|
||||
slint::private_api::drop_in_place<{component_id}>, slint::private_api::dealloc }}"
|
||||
)),
|
||||
..Default::default()
|
||||
}));
|
||||
|
||||
let create_code = vec![
|
||||
format!("slint::SharedVector<slint::SharedString> include_paths{{ {} }};", compiler_config.include_paths.iter().map(|p| format!("\"{}\"", escape_string(&p.to_string_lossy()))).join(", ")),
|
||||
format!("slint::SharedVector<slint::SharedString> library_paths{{ {} }};", compiler_config.library_paths.iter().map(|(l, p)| format!("\"{l}={}\"", p.to_string_lossy())).join(", ")),
|
||||
format!("auto live_reload = slint::private_api::live_reload::LiveReloadingComponent({main_file:?}, {:?}, include_paths, library_paths, \"{}\");", component.name, compiler_config.style.as_ref().unwrap_or(&String::new())),
|
||||
format!("auto self_rc = vtable::VRc<slint::private_api::ItemTreeVTable, {component_id}>::make(std::move(live_reload));"),
|
||||
format!("return slint::ComponentHandle<{component_id}>(self_rc);"),
|
||||
];
|
||||
|
||||
component_struct.members.push((
|
||||
Access::Public,
|
||||
Declaration::Function(Function {
|
||||
name: "create".into(),
|
||||
signature: format!("() -> slint::ComponentHandle<{component_id}>"),
|
||||
statements: Some(create_code),
|
||||
is_static: true,
|
||||
..Default::default()
|
||||
}),
|
||||
));
|
||||
|
||||
component_struct.members.push((
|
||||
Access::Public,
|
||||
Declaration::Function(Function {
|
||||
is_constructor_or_destructor: true,
|
||||
name: ident(&component_struct.name),
|
||||
signature: "(slint::private_api::live_reload::LiveReloadingComponent live_reload)"
|
||||
.into(),
|
||||
constructor_member_initializers: vec!["live_reload(std::move(live_reload))".into()],
|
||||
statements: Some(vec![]),
|
||||
..Default::default()
|
||||
}),
|
||||
));
|
||||
|
||||
component_struct.members.push((
|
||||
Access::Public,
|
||||
Declaration::Function(Function {
|
||||
name: "show".into(),
|
||||
signature: "() -> void".into(),
|
||||
statements: Some(vec!["window().show();".into()]),
|
||||
..Default::default()
|
||||
}),
|
||||
));
|
||||
|
||||
component_struct.members.push((
|
||||
Access::Public,
|
||||
Declaration::Function(Function {
|
||||
name: "hide".into(),
|
||||
signature: "() -> void".into(),
|
||||
statements: Some(vec!["window().hide();".into()]),
|
||||
..Default::default()
|
||||
}),
|
||||
));
|
||||
|
||||
component_struct.members.push((
|
||||
Access::Public,
|
||||
Declaration::Function(Function {
|
||||
name: "window".into(),
|
||||
signature: "() const -> slint::Window&".into(),
|
||||
statements: Some(vec!["return live_reload.window();".into()]),
|
||||
..Default::default()
|
||||
}),
|
||||
));
|
||||
|
||||
component_struct.members.push((
|
||||
Access::Public,
|
||||
Declaration::Function(Function {
|
||||
name: "run".into(),
|
||||
signature: "() -> void".into(),
|
||||
statements: Some(vec![
|
||||
"show();".into(),
|
||||
"slint::run_event_loop();".into(),
|
||||
"hide();".into(),
|
||||
]),
|
||||
..Default::default()
|
||||
}),
|
||||
));
|
||||
|
||||
file.definitions.extend(component_struct.extract_definitions().collect::<Vec<_>>());
|
||||
file.declarations.push(Declaration::Struct(component_struct));
|
||||
}
|
||||
|
||||
fn generate_global(file: &mut File, global: &llr::GlobalComponent) {
|
||||
let mut global_struct = Struct { name: ident(&global.name), ..Default::default() };
|
||||
|
||||
global_struct.members.push((
|
||||
Access::Private,
|
||||
Declaration::Var(Var {
|
||||
ty: "const slint::private_api::live_reload::LiveReloadingComponent&".into(),
|
||||
name: "live_reload".into(),
|
||||
..Default::default()
|
||||
}),
|
||||
));
|
||||
|
||||
global_struct.members.push((
|
||||
Access::Public,
|
||||
Declaration::Function(Function {
|
||||
is_constructor_or_destructor: true,
|
||||
name: ident(&global.name),
|
||||
signature:
|
||||
"(const slint::private_api::live_reload::LiveReloadingComponent &live_reload)"
|
||||
.into(),
|
||||
constructor_member_initializers: vec!["live_reload(live_reload)".into()],
|
||||
statements: Some(vec![]),
|
||||
..Default::default()
|
||||
}),
|
||||
));
|
||||
|
||||
generate_public_api_for_properties(
|
||||
&format!("{}.", global.name),
|
||||
&mut global_struct.members,
|
||||
&global.public_properties,
|
||||
&global.private_properties,
|
||||
);
|
||||
|
||||
file.definitions.extend(global_struct.extract_definitions().collect::<Vec<_>>());
|
||||
file.declarations.push(Declaration::Struct(global_struct));
|
||||
}
|
||||
|
||||
fn generate_public_api_for_properties(
|
||||
prefix: &str,
|
||||
declarations: &mut Vec<(Access, Declaration)>,
|
||||
public_properties: &llr::PublicProperties,
|
||||
private_properties: &llr::PrivateProperties,
|
||||
) {
|
||||
for p in public_properties {
|
||||
let prop_name = &p.name;
|
||||
let prop_ident = concatenate_ident(prop_name);
|
||||
|
||||
if let Type::Callback(callback) = &p.ty {
|
||||
let ret = callback.return_type.cpp_type().unwrap();
|
||||
let param_types =
|
||||
callback.args.iter().map(|t| t.cpp_type().unwrap()).collect::<Vec<_>>();
|
||||
let callback_emitter = vec![format!(
|
||||
"return {}(live_reload.invoke(\"{prefix}{prop_name}\" {}));",
|
||||
convert_from_value_fn(&callback.return_type),
|
||||
(0..callback.args.len()).map(|i| format!(", arg_{i}")).join(""),
|
||||
)];
|
||||
declarations.push((
|
||||
Access::Public,
|
||||
Declaration::Function(Function {
|
||||
name: format_smolstr!("invoke_{prop_ident}"),
|
||||
signature: format!(
|
||||
"({}) const -> {ret}",
|
||||
param_types
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, ty)| format!("{ty} arg_{i}"))
|
||||
.join(", "),
|
||||
),
|
||||
statements: Some(callback_emitter),
|
||||
..Default::default()
|
||||
}),
|
||||
));
|
||||
let args = callback
|
||||
.args
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, t)| format!("{}(args[{i}])", convert_from_value_fn(t)))
|
||||
.join(", ");
|
||||
let return_statement = if callback.return_type == Type::Void {
|
||||
format!("callback_handler({args}); return slint::interpreter::Value();",)
|
||||
} else {
|
||||
format!(
|
||||
"return {}(callback_handler({args}));",
|
||||
convert_to_value_fn(&callback.return_type),
|
||||
)
|
||||
};
|
||||
declarations.push((
|
||||
Access::Public,
|
||||
Declaration::Function(Function {
|
||||
name: format_smolstr!("on_{}", concatenate_ident(&p.name)),
|
||||
template_parameters: Some(format!(
|
||||
"std::invocable<{}> Functor",
|
||||
param_types.join(", "),
|
||||
)),
|
||||
signature: "(Functor && callback_handler) const".into(),
|
||||
statements: Some(vec {{ {return_statement} }});",
|
||||
),
|
||||
]),
|
||||
..Default::default()
|
||||
}),
|
||||
));
|
||||
} else if let Type::Function(function) = &p.ty {
|
||||
let param_types =
|
||||
function.args.iter().map(|t| t.cpp_type().unwrap()).collect::<Vec<_>>();
|
||||
let ret = function.return_type.cpp_type().unwrap();
|
||||
let call_code = vec![format!(
|
||||
"return {}(live_reload.invoke(\"{prefix}{prop_name}\"{}));",
|
||||
convert_from_value_fn(&function.return_type),
|
||||
(0..function.args.len()).map(|i| format!(", arg_{i}")).join("")
|
||||
)];
|
||||
declarations.push((
|
||||
Access::Public,
|
||||
Declaration::Function(Function {
|
||||
name: format_smolstr!("invoke_{}", concatenate_ident(&p.name)),
|
||||
signature: format!(
|
||||
"({}) const -> {ret}",
|
||||
param_types
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, ty)| format!("{ty} arg_{i}"))
|
||||
.join(", "),
|
||||
),
|
||||
statements: Some(call_code),
|
||||
..Default::default()
|
||||
}),
|
||||
));
|
||||
} else {
|
||||
let cpp_property_type = p.ty.cpp_type().expect("Invalid type in public properties");
|
||||
let prop_getter: Vec<String> = vec![format!(
|
||||
"return {}(live_reload.get_property(\"{prefix}{prop_name}\"));",
|
||||
convert_from_value_fn(&p.ty)
|
||||
)];
|
||||
declarations.push((
|
||||
Access::Public,
|
||||
Declaration::Function(Function {
|
||||
name: format_smolstr!("get_{}", &prop_ident),
|
||||
signature: format!("() const -> {cpp_property_type}"),
|
||||
statements: Some(prop_getter),
|
||||
..Default::default()
|
||||
}),
|
||||
));
|
||||
|
||||
if !p.read_only {
|
||||
let prop_setter: Vec<String> = vec![
|
||||
"using slint::private_api::live_reload::into_slint_value;".into(),
|
||||
format!(
|
||||
"live_reload.set_property(\"{prefix}{prop_name}\", {}(value));",
|
||||
convert_to_value_fn(&p.ty)
|
||||
),
|
||||
];
|
||||
declarations.push((
|
||||
Access::Public,
|
||||
Declaration::Function(Function {
|
||||
name: format_smolstr!("set_{}", &prop_ident),
|
||||
signature: format!("(const {} &value) const -> void", cpp_property_type),
|
||||
statements: Some(prop_setter),
|
||||
..Default::default()
|
||||
}),
|
||||
));
|
||||
} else {
|
||||
declarations.push((
|
||||
Access::Private,
|
||||
Declaration::Function(Function {
|
||||
name: format_smolstr!("set_{}", &prop_ident),
|
||||
signature: format!(
|
||||
"(const {cpp_property_type} &) const = delete /* property '{}' is declared as 'out' (read-only). Declare it as 'in' or 'in-out' to enable the setter */", p.name
|
||||
),
|
||||
..Default::default()
|
||||
}),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (name, ty) in private_properties {
|
||||
let prop_ident = concatenate_ident(name);
|
||||
|
||||
if let Type::Function(function) = &ty {
|
||||
let param_types = function.args.iter().map(|t| t.cpp_type().unwrap()).join(", ");
|
||||
declarations.push((
|
||||
Access::Private,
|
||||
Declaration::Function(Function {
|
||||
name: format_smolstr!("invoke_{prop_ident}"),
|
||||
signature: format!(
|
||||
"({param_types}) const = delete /* the function '{name}' is declared as private. Declare it as 'public' */",
|
||||
),
|
||||
..Default::default()
|
||||
}),
|
||||
));
|
||||
} else {
|
||||
declarations.push((
|
||||
Access::Private,
|
||||
Declaration::Function(Function {
|
||||
name: format_smolstr!("get_{prop_ident}"),
|
||||
signature: format!(
|
||||
"() const = delete /* the property '{name}' is declared as private. Declare it as 'in', 'out', or 'in-out' to make it public */",
|
||||
),
|
||||
..Default::default()
|
||||
}),
|
||||
));
|
||||
declarations.push((
|
||||
Access::Private,
|
||||
Declaration::Function(Function {
|
||||
name: format_smolstr!("set_{}", &prop_ident),
|
||||
signature: format!(
|
||||
"(const auto &) const = delete /* property '{name}' is declared as private. Declare it as 'in' or 'in-out' to make it public */",
|
||||
),
|
||||
..Default::default()
|
||||
}),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_to_value_fn(ty: &Type) -> String {
|
||||
match ty {
|
||||
Type::Struct(s) if s.name.is_none() => {
|
||||
let mut init = s.fields.iter().enumerate().map(|(i, (name, ty))| {
|
||||
format!(
|
||||
"s.set_field(\"{name}\", {}(std::get<{i}>(tuple))); ",
|
||||
convert_to_value_fn(ty)
|
||||
)
|
||||
});
|
||||
format!("([](const auto &tuple) {{ slint::interpreter::Struct s; {}return slint::interpreter::Value(s); }})", init.join(""))
|
||||
}
|
||||
_ => "into_slint_value".into(),
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_from_value_fn(ty: &Type) -> String {
|
||||
match ty {
|
||||
Type::Struct(s) if s.name.is_none() => {
|
||||
let mut init = s.fields.iter().map(|(name, ty)| {
|
||||
format!("slint::private_api::live_reload::from_slint_value<{}>(s.get_field(\"{name}\").value())", ty.cpp_type().unwrap())
|
||||
});
|
||||
format!(
|
||||
"([](const slint::interpreter::Value &v) {{ auto s = v.to_struct().value(); return std::make_tuple({}); }})",
|
||||
init.join(", ")
|
||||
)
|
||||
}
|
||||
_ => format!(
|
||||
"slint::private_api::live_reload::from_slint_value<{}>",
|
||||
ty.cpp_type().unwrap_or_default()
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_value_conversions(file: &mut File, structs_and_enums: &[Type]) {
|
||||
for ty in structs_and_enums {
|
||||
match ty {
|
||||
Type::Struct(s) if s.name.is_some() && s.node.is_some() => {
|
||||
let name = ident(&s.name.as_ref().unwrap());
|
||||
let mut to_statements = vec![
|
||||
"using slint::private_api::live_reload::into_slint_value;".into(),
|
||||
"slint::interpreter::Struct s;".into(),
|
||||
];
|
||||
let mut from_statements = vec![
|
||||
"using slint::private_api::live_reload::from_slint_value;".into(),
|
||||
"slint::interpreter::Struct s = val.to_struct().value();".into(),
|
||||
format!("{name} self;"),
|
||||
];
|
||||
for (f, t) in &s.fields {
|
||||
to_statements.push(format!(
|
||||
"s.set_field(\"{f}\", into_slint_value(self.{}));",
|
||||
ident(f)
|
||||
));
|
||||
from_statements.push(format!(
|
||||
"self.{} = slint::private_api::live_reload::from_slint_value<{}>(s.get_field(\"{f}\").value());",
|
||||
ident(f),
|
||||
t.cpp_type().unwrap()
|
||||
));
|
||||
}
|
||||
to_statements.push("return s;".into());
|
||||
from_statements.push("return self;".into());
|
||||
file.declarations.push(Declaration::Function(Function {
|
||||
name: "into_slint_value".into(),
|
||||
signature: format!(
|
||||
"([[maybe_unused]] const {name} &self) -> slint::interpreter::Value"
|
||||
),
|
||||
statements: Some(to_statements),
|
||||
is_inline: true,
|
||||
..Function::default()
|
||||
}));
|
||||
file.declarations.push(Declaration::Function(Function {
|
||||
name: "from_slint_value".into(),
|
||||
signature: format!(
|
||||
"(const slint::interpreter::Value &val, const {name} *) -> {name}"
|
||||
),
|
||||
statements: Some(from_statements),
|
||||
is_inline: true,
|
||||
..Function::default()
|
||||
}));
|
||||
}
|
||||
Type::Enumeration(_) => {
|
||||
// todo;
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1227,7 +1227,7 @@ pub fn print_diagnostics(diagnostics: &[Diagnostic]) {
|
|||
/// An instance can be put on screen with the [`ComponentInstance::run`] function.
|
||||
#[repr(C)]
|
||||
pub struct ComponentInstance {
|
||||
inner: crate::dynamic_item_tree::DynamicComponentVRc,
|
||||
pub(crate) inner: crate::dynamic_item_tree::DynamicComponentVRc,
|
||||
}
|
||||
|
||||
impl ComponentInstance {
|
||||
|
|
|
@ -327,7 +327,7 @@ pub unsafe extern "C" fn slint_interpreter_component_instance_invoke(
|
|||
///
|
||||
/// Safety: user_data must be a pointer that can be destroyed by the drop_user_data function.
|
||||
/// callback must be a valid callback that initialize the `ret`
|
||||
struct CallbackUserData {
|
||||
pub struct CallbackUserData {
|
||||
user_data: *mut c_void,
|
||||
drop_user_data: Option<extern "C" fn(*mut c_void)>,
|
||||
callback: extern "C" fn(user_data: *mut c_void, arg: Slice<Box<Value>>) -> Box<Value>,
|
||||
|
@ -342,7 +342,15 @@ impl Drop for CallbackUserData {
|
|||
}
|
||||
|
||||
impl CallbackUserData {
|
||||
fn call(&self, args: &[Value]) -> Value {
|
||||
pub unsafe fn new(
|
||||
user_data: *mut c_void,
|
||||
drop_user_data: Option<extern "C" fn(*mut c_void)>,
|
||||
callback: extern "C" fn(user_data: *mut c_void, arg: Slice<Box<Value>>) -> Box<Value>,
|
||||
) -> Self {
|
||||
Self { user_data, drop_user_data, callback }
|
||||
}
|
||||
|
||||
pub fn call(&self, args: &[Value]) -> Value {
|
||||
let args = args.iter().map(|v| v.clone().into()).collect::<Vec<_>>();
|
||||
(self.callback)(self.user_data, Slice::from_slice(args.as_ref())).as_ref().clone()
|
||||
}
|
||||
|
@ -358,7 +366,7 @@ pub unsafe extern "C" fn slint_interpreter_component_instance_set_callback(
|
|||
user_data: *mut c_void,
|
||||
drop_user_data: Option<extern "C" fn(*mut c_void)>,
|
||||
) -> bool {
|
||||
let ud = CallbackUserData { user_data, drop_user_data, callback };
|
||||
let ud = CallbackUserData::new(user_data, drop_user_data, callback);
|
||||
|
||||
generativity::make_guard!(guard);
|
||||
let comp = inst.unerase(guard);
|
||||
|
@ -424,7 +432,7 @@ pub unsafe extern "C" fn slint_interpreter_component_instance_set_global_callbac
|
|||
user_data: *mut c_void,
|
||||
drop_user_data: Option<extern "C" fn(*mut c_void)>,
|
||||
) -> bool {
|
||||
let ud = CallbackUserData { user_data, drop_user_data, callback };
|
||||
let ud = CallbackUserData::new(user_data, drop_user_data, callback);
|
||||
|
||||
generativity::make_guard!(guard);
|
||||
let comp = inst.unerase(guard);
|
||||
|
|
|
@ -303,3 +303,137 @@ impl Watcher {
|
|||
self.files.insert(path.into());
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "ffi")]
|
||||
mod ffi {
|
||||
use super::*;
|
||||
use core::ffi::c_void;
|
||||
use i_slint_core::window::WindowAdapter;
|
||||
use i_slint_core::{slice::Slice, SharedString, SharedVector};
|
||||
type LiveReloadingComponentInner = RefCell<LiveReloadingComponent>;
|
||||
|
||||
#[no_mangle]
|
||||
/// LibraryPath is an array of string that have in the form `lib=...`
|
||||
pub extern "C" fn slint_live_reload_new(
|
||||
file_name: Slice<u8>,
|
||||
component_name: Slice<u8>,
|
||||
include_paths: &SharedVector<SharedString>,
|
||||
library_paths: &SharedVector<SharedString>,
|
||||
style: Slice<u8>,
|
||||
) -> *const LiveReloadingComponentInner {
|
||||
let mut compiler = Compiler::default();
|
||||
compiler.set_include_paths(
|
||||
include_paths.iter().map(|path| PathBuf::from(path.as_str())).collect(),
|
||||
);
|
||||
compiler.set_library_paths(
|
||||
library_paths
|
||||
.iter()
|
||||
.map(|path| path.as_str().split_once('=').expect("library path must have an '='"))
|
||||
.map(|(lib, path)| (lib.into(), PathBuf::from(path)))
|
||||
.collect(),
|
||||
);
|
||||
if !style.is_empty() {
|
||||
compiler.set_style(std::str::from_utf8(&style).unwrap().into());
|
||||
}
|
||||
Rc::into_raw(
|
||||
LiveReloadingComponent::new(
|
||||
compiler,
|
||||
std::path::PathBuf::from(std::str::from_utf8(&file_name).unwrap()),
|
||||
std::str::from_utf8(&component_name).unwrap().into(),
|
||||
)
|
||||
.expect("Creating the component failed"),
|
||||
)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn slint_live_reload_clone(
|
||||
component: *const LiveReloadingComponentInner,
|
||||
) {
|
||||
Rc::increment_strong_count(component);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn slint_live_reload_drop(component: *const LiveReloadingComponentInner) {
|
||||
Rc::decrement_strong_count(component);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn slint_live_reload_set_property(
|
||||
component: &LiveReloadingComponentInner,
|
||||
property: Slice<u8>,
|
||||
value: &Value,
|
||||
) {
|
||||
let property = std::str::from_utf8(&property).unwrap();
|
||||
if let Some((global, prop)) = property.split_once('.') {
|
||||
component.borrow_mut().set_global_property(global, prop, value.clone());
|
||||
} else {
|
||||
component.borrow_mut().set_property(property, value.clone());
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn slint_live_reload_get_property(
|
||||
component: &LiveReloadingComponentInner,
|
||||
property: Slice<u8>,
|
||||
) -> *mut Value {
|
||||
let property = std::str::from_utf8(&property).unwrap();
|
||||
let val = if let Some((global, prop)) = property.split_once('.') {
|
||||
component.borrow().get_global_property(global, prop)
|
||||
} else {
|
||||
component.borrow().get_property(property)
|
||||
};
|
||||
Box::into_raw(Box::new(val))
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn slint_live_reload_invoke(
|
||||
component: &LiveReloadingComponentInner,
|
||||
callback: Slice<u8>,
|
||||
args: Slice<Box<Value>>,
|
||||
) -> *mut Value {
|
||||
let callback = std::str::from_utf8(&callback).unwrap();
|
||||
let args = args.iter().map(|vb| vb.as_ref().clone()).collect::<Vec<_>>();
|
||||
let val = if let Some((global, prop)) = callback.split_once('.') {
|
||||
component.borrow().invoke_global(global, prop, &args)
|
||||
} else {
|
||||
component.borrow().invoke(callback, &args)
|
||||
};
|
||||
Box::into_raw(Box::new(val))
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn slint_live_reload_set_callback(
|
||||
component: &LiveReloadingComponentInner,
|
||||
callback: Slice<u8>,
|
||||
callback_handler: extern "C" fn(
|
||||
user_data: *mut c_void,
|
||||
arg: Slice<Box<Value>>,
|
||||
) -> Box<Value>,
|
||||
user_data: *mut c_void,
|
||||
drop_user_data: Option<extern "C" fn(*mut c_void)>,
|
||||
) {
|
||||
let ud = crate::ffi::CallbackUserData::new(user_data, drop_user_data, callback_handler);
|
||||
let handler = Rc::new(move |args: &[Value]| ud.call(args));
|
||||
let callback = std::str::from_utf8(&callback).unwrap();
|
||||
if let Some((global, prop)) = callback.split_once('.') {
|
||||
component.borrow_mut().set_global_callback(global, prop, handler);
|
||||
} else {
|
||||
component.borrow_mut().set_callback(callback, handler);
|
||||
}
|
||||
}
|
||||
|
||||
/// Same precondition as slint_interpreter_component_instance_window
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn slint_live_reload_window(
|
||||
component: &LiveReloadingComponentInner,
|
||||
out: *mut *const i_slint_core::window::ffi::WindowAdapterRcOpaque,
|
||||
) {
|
||||
assert_eq!(
|
||||
core::mem::size_of::<Rc<dyn WindowAdapter>>(),
|
||||
core::mem::size_of::<i_slint_core::window::ffi::WindowAdapterRcOpaque>()
|
||||
);
|
||||
let borrow = component.borrow();
|
||||
let adapter = borrow.instance().inner.window_adapter_ref().unwrap();
|
||||
core::ptr::write(out as *mut *const Rc<dyn WindowAdapter>, adapter as *const _)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue