slint/api/cpp/include/slint.h
Simon Hausmann 06a19e59ad
slint: Change Platform.os to return a new enum OperatingSystemType (#8679)
* slint: Change Platform.os to return a new enum OperatingSystemType

cc #8631
2025-06-12 14:25:16 +02:00

500 lines
18 KiB
C++

// 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
#if defined(__GNUC__) || defined(__clang__)
// In C++17, it is conditionally supported, but still valid for all compiler we care
# pragma GCC diagnostic ignored "-Winvalid-offsetof"
#endif
#include "slint_internal.h"
#include "slint_platform_internal.h"
#include "slint_qt_internal.h"
#include "slint_window.h"
#include "slint_models.h"
#include "slint_item_tree.h"
#include <vector>
#include <chrono>
#include <span>
#include <concepts>
#ifndef SLINT_FEATURE_FREESTANDING
# include <mutex>
# include <condition_variable>
#endif
/// \rst
/// The :code:`slint` namespace is the primary entry point into the Slint C++ API.
/// All available types are in this namespace.
///
/// See the :doc:`Overview <../overview>` documentation for the C++ integration how
/// to load :code:`.slint` designs.
/// \endrst
namespace slint {
namespace private_api {
/// Convert a slint `{height: length, width: length, x: length, y: length}` to a Rect
inline cbindgen_private::Rect convert_anonymous_rect(std::tuple<float, float, float, float> tuple)
{
// alphabetical order
auto [h, w, x, y] = tuple;
return cbindgen_private::Rect { .x = x, .y = y, .width = w, .height = h };
}
inline void dealloc(const ItemTreeVTable *, uint8_t *ptr, [[maybe_unused]] vtable::Layout layout)
{
#ifdef __cpp_sized_deallocation
::operator delete(reinterpret_cast<void *>(ptr), layout.size,
static_cast<std::align_val_t>(layout.align));
#elif !defined(__APPLE__) || MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_14
::operator delete(reinterpret_cast<void *>(ptr), static_cast<std::align_val_t>(layout.align));
#else
::operator delete(reinterpret_cast<void *>(ptr));
#endif
}
template<typename T>
inline vtable::Layout drop_in_place(ItemTreeRef item_tree)
{
reinterpret_cast<T *>(item_tree.instance)->~T();
return vtable::Layout { sizeof(T), alignof(T) };
}
#if !defined(DOXYGEN)
# if defined(_WIN32) || defined(_WIN64)
// On Windows cross-dll data relocations are not supported:
// https://docs.microsoft.com/en-us/cpp/c-language/rules-and-limitations-for-dllimport-dllexport?view=msvc-160
// so we have a relocation to a function that returns the address we seek. That
// relocation will be resolved to the locally linked stub library, the implementation of
// which will be patched.
# define SLINT_GET_ITEM_VTABLE(VTableName) slint::private_api::slint_get_##VTableName()
# else
# define SLINT_GET_ITEM_VTABLE(VTableName) (&slint::private_api::VTableName)
# endif
#endif // !defined(DOXYGEN)
inline std::optional<cbindgen_private::ItemRc>
upgrade_item_weak(const cbindgen_private::ItemWeak &item_weak)
{
if (auto item_tree_strong = item_weak.item_tree.lock()) {
return { { *item_tree_strong, item_weak.index } };
} else {
return std::nullopt;
}
}
inline void debug(const SharedString &str)
{
cbindgen_private::slint_debug(&str);
}
} // namespace private_api
namespace cbindgen_private {
inline LayoutInfo LayoutInfo::merge(const LayoutInfo &other) const
{
// Note: This "logic" is duplicated from LayoutInfo::merge in layout.rs.
return LayoutInfo { std::min(max, other.max),
std::min(max_percent, other.max_percent),
std::max(min, other.min),
std::max(min_percent, other.min_percent),
std::max(preferred, other.preferred),
std::min(stretch, other.stretch) };
}
inline bool operator==(const EasingCurve &a, const EasingCurve &b)
{
if (a.tag != b.tag) {
return false;
} else if (a.tag == EasingCurve::Tag::CubicBezier) {
return std::equal(a.cubic_bezier._0, a.cubic_bezier._0 + 4, b.cubic_bezier._0);
}
return true;
}
}
namespace private_api {
inline static void register_item_tree(const vtable::VRc<ItemTreeVTable> *c,
const std::optional<slint::Window> &maybe_window)
{
const cbindgen_private::WindowAdapterRcOpaque *window_ptr =
maybe_window.has_value() ? &maybe_window->window_handle().handle() : nullptr;
cbindgen_private::slint_register_item_tree(c, window_ptr);
}
inline SharedVector<float> solve_box_layout(const cbindgen_private::BoxLayoutData &data,
cbindgen_private::Slice<int> repeater_indexes)
{
SharedVector<float> result;
cbindgen_private::Slice<uint32_t> ri { reinterpret_cast<uint32_t *>(repeater_indexes.ptr),
repeater_indexes.len };
cbindgen_private::slint_solve_box_layout(&data, ri, &result);
return result;
}
inline SharedVector<float> solve_grid_layout(const cbindgen_private::GridLayoutData &data)
{
SharedVector<float> result;
cbindgen_private::slint_solve_grid_layout(&data, &result);
return result;
}
inline cbindgen_private::LayoutInfo
grid_layout_info(cbindgen_private::Slice<cbindgen_private::GridLayoutCellData> cells, float spacing,
const cbindgen_private::Padding &padding)
{
return cbindgen_private::slint_grid_layout_info(cells, spacing, &padding);
}
inline cbindgen_private::LayoutInfo
box_layout_info(cbindgen_private::Slice<cbindgen_private::BoxLayoutCellData> cells, float spacing,
const cbindgen_private::Padding &padding,
cbindgen_private::LayoutAlignment alignment)
{
return cbindgen_private::slint_box_layout_info(cells, spacing, &padding, alignment);
}
inline cbindgen_private::LayoutInfo
box_layout_info_ortho(cbindgen_private::Slice<cbindgen_private::BoxLayoutCellData> cells,
const cbindgen_private::Padding &padding)
{
return cbindgen_private::slint_box_layout_info_ortho(cells, &padding);
}
/// Access the layout cache of an item within a repeater
inline float layout_cache_access(const SharedVector<float> &cache, int offset, int repeater_index)
{
size_t idx = size_t(cache[offset]) + repeater_index * 2;
return idx < cache.size() ? cache[idx] : 0;
}
template<typename VT, typename ItemType>
inline cbindgen_private::LayoutInfo
item_layout_info(VT *itemvtable, ItemType *item_ptr, cbindgen_private::Orientation orientation,
WindowAdapterRc *window_adapter, const ItemTreeRc &component_rc,
uint32_t item_index)
{
cbindgen_private::ItemRc item_rc { component_rc, item_index };
return itemvtable->layout_info({ itemvtable, item_ptr }, orientation, window_adapter, &item_rc);
}
} // namespace private_api
namespace private_api {
inline void setup_popup_menu_from_menu_item_tree(
const ItemTreeRc &menu_item_tree,
Property<std::shared_ptr<Model<cbindgen_private::MenuEntry>>> &entries,
Callback<std::shared_ptr<Model<cbindgen_private::MenuEntry>>(cbindgen_private::MenuEntry)>
&sub_menu,
Callback<void(cbindgen_private::MenuEntry)> &activated)
{
using cbindgen_private::MenuEntry;
using cbindgen_private::MenuVTable;
auto shared = std::make_shared<vtable::VBox<MenuVTable>>(nullptr, nullptr);
cbindgen_private::slint_menus_create_wrapper(&menu_item_tree, &*shared);
entries.set_binding([shared] {
vtable::VRefMut<MenuVTable> ref { shared->vtable, shared->instance };
SharedVector<MenuEntry> entries_sv;
shared->vtable->sub_menu(ref, nullptr, &entries_sv);
std::vector<MenuEntry> entries_vec(entries_sv.begin(), entries_sv.end());
return std::make_shared<VectorModel<MenuEntry>>(std::move(entries_vec));
});
sub_menu.set_handler([shared](const auto &entry) {
vtable::VRefMut<MenuVTable> ref { shared->vtable, shared->instance };
SharedVector<MenuEntry> entries_sv;
shared->vtable->sub_menu(ref, &entry, &entries_sv);
std::vector<MenuEntry> entries_vec(entries_sv.begin(), entries_sv.end());
return std::make_shared<VectorModel<MenuEntry>>(std::move(entries_vec));
});
activated.set_handler([shared](const auto &entry) {
vtable::VRefMut<MenuVTable> ref { shared->vtable, shared->instance };
shared->vtable->activate(ref, &entry);
});
}
inline SharedString translate(const SharedString &original, const SharedString &context,
const SharedString &domain,
cbindgen_private::Slice<SharedString> arguments, int n,
const SharedString &plural)
{
SharedString result = original;
cbindgen_private::slint_translate(&result, &context, &domain, arguments, n, &plural);
return result;
}
inline SharedString translate_from_bundle(std::span<const char8_t *const> strs,
cbindgen_private::Slice<SharedString> arguments)
{
SharedString result;
cbindgen_private::slint_translate_from_bundle(
cbindgen_private::Slice<const char *>(
const_cast<char const **>(reinterpret_cast<char const *const *>(strs.data())),
strs.size()),
arguments, &result);
return result;
}
inline SharedString
translate_from_bundle_with_plural(std::span<const char8_t *const> strs,
std::span<const uint32_t> indices,
std::span<uintptr_t (*const)(int32_t)> plural_rules,
cbindgen_private::Slice<SharedString> arguments, int n)
{
SharedString result;
cbindgen_private::Slice<const char *> strs_slice(
const_cast<char const **>(reinterpret_cast<char const *const *>(strs.data())),
strs.size());
cbindgen_private::Slice<uint32_t> indices_slice(
const_cast<uint32_t *>(reinterpret_cast<const uint32_t *>(indices.data())),
indices.size());
cbindgen_private::Slice<uintptr_t (*)(int32_t)> plural_rules_slice(
const_cast<uintptr_t (**)(int32_t)>(
reinterpret_cast<uintptr_t (*const *)(int32_t)>(plural_rules.data())),
plural_rules.size());
cbindgen_private::slint_translate_from_bundle_with_plural(
strs_slice, indices_slice, plural_rules_slice, arguments, n, &result);
return result;
}
} // namespace private_api
#ifdef SLINT_FEATURE_GETTEXT
/// Forces all the strings that are translated with `@tr(...)` to be re-evaluated.
/// This is useful if the language is changed at runtime.
/// The function is only available when Slint is compiled with `SLINT_FEATURE_GETTEXT`.
///
/// Example
/// ```cpp
/// my_ui->global<LanguageSettings>().on_french_selected([] {
/// setenv("LANGUAGE", langs[l], true);
/// slint::update_all_translations();
/// });
/// ```
inline void update_all_translations()
{
cbindgen_private::slint_translations_mark_dirty();
}
#endif
/// Select the current translation language when using bundled translations.
/// This function requires that the application's `.slint` file was compiled with bundled
/// translations. It must be called after creating the first component.
///
/// The language string is the locale, which matches the name of the folder that contains the
/// `LC_MESSAGES` folder. An empty string or `"en"` will select the default language.
///
/// Returns true if the language was selected; false if the language was not found in the list of
/// bundled translations.
inline bool select_bundled_translation(std::string_view language)
{
return cbindgen_private::slint_translate_select_bundled_translation(
slint::private_api::string_to_slice(language));
}
#if !defined(DOXYGEN)
cbindgen_private::Flickable::Flickable()
{
slint_flickable_data_init(&data);
}
cbindgen_private::Flickable::~Flickable()
{
slint_flickable_data_free(&data);
}
cbindgen_private::NativeStyleMetrics::NativeStyleMetrics(void *)
{
slint_native_style_metrics_init(this);
}
cbindgen_private::NativeStyleMetrics::~NativeStyleMetrics()
{
slint_native_style_metrics_deinit(this);
}
cbindgen_private::NativePalette::NativePalette(void *)
{
slint_native_palette_init(this);
}
cbindgen_private::NativePalette::~NativePalette()
{
slint_native_palette_deinit(this);
}
#endif // !defined(DOXYGEN)
namespace private_api {
// Was used in Slint <= 1.1.0 to have an error message in case of mismatch
template<int Major, int Minor, int Patch>
struct [[deprecated]] VersionCheckHelper
{
};
}
/// Enum for the event loop mode parameter of the slint::run_event_loop() function.
/// It is used to determine when the event loop quits.
enum class EventLoopMode {
/// The event loop will quit when the last window is closed
/// or when slint::quit_event_loop() is called.
QuitOnLastWindowClosed,
/// The event loop will keep running until slint::quit_event_loop() is called,
/// even when all windows are closed.
RunUntilQuit
};
/// Enters the main event loop. This is necessary in order to receive
/// events from the windowing system in order to render to the screen
/// and react to user input.
///
/// The mode parameter determines the behavior of the event loop when all windows are closed.
/// By default, it is set to QuitOnLastWindowClose, which means the event loop will
/// quit when the last window is closed.
inline void run_event_loop(EventLoopMode mode = EventLoopMode::QuitOnLastWindowClosed)
{
private_api::assert_main_thread();
cbindgen_private::slint_run_event_loop(mode == EventLoopMode::QuitOnLastWindowClosed);
}
/// Schedules the main event loop for termination. This function is meant
/// to be called from callbacks triggered by the UI. After calling the function,
/// it will return immediately and once control is passed back to the event loop,
/// the initial call to slint::run_event_loop() will return.
inline void quit_event_loop()
{
cbindgen_private::slint_quit_event_loop();
}
/// Adds the specified functor to an internal queue, notifies the event loop to wake up.
/// Once woken up, any queued up functors will be invoked.
/// This function is thread-safe and can be called from any thread, including the one
/// running the event loop. The provided functors will only be invoked from the thread
/// that started the event loop.
///
/// You can use this to set properties or use any other Slint APIs from other threads,
/// by collecting the code in a functor and queuing it up for invocation within the event loop.
///
/// The following example assumes that a status message received from a network thread is
/// shown in the UI:
///
/// ```
/// #include "my_application_ui.h"
/// #include <thread>
///
/// int main(int argc, char **argv)
/// {
/// auto ui = NetworkStatusUI::create();
/// ui->set_status_label("Pending");
///
/// slint::ComponentWeakHandle<NetworkStatusUI> weak_ui_handle(ui);
/// std::thread network_thread([=]{
/// std::string message = read_message_blocking_from_network();
/// slint::invoke_from_event_loop([&]() {
/// if (auto ui = weak_ui_handle.lock()) {
/// ui->set_status_label(message);
/// }
/// });
/// });
/// ...
/// ui->run();
/// ...
/// }
/// ```
///
/// See also blocking_invoke_from_event_loop() for a blocking version of this function
template<std::invocable Functor>
void invoke_from_event_loop(Functor f)
{
cbindgen_private::slint_post_event(
[](void *data) { (*reinterpret_cast<Functor *>(data))(); }, new Functor(std::move(f)),
[](void *data) { delete reinterpret_cast<Functor *>(data); });
}
#if !defined(SLINT_FEATURE_FREESTANDING) || defined(DOXYGEN)
/// Blocking version of invoke_from_event_loop()
///
/// Just like invoke_from_event_loop(), this will run the specified functor from the thread running
/// the slint event loop. But it will block until the execution of the functor is finished,
/// and return that value.
///
/// This function must be called from a different thread than the thread that runs the event loop
/// otherwise it will result in a deadlock. Calling this function if the event loop is not running
/// will also block forever or until the event loop is started in another thread.
///
/// The following example is reading the message property from a thread
///
/// ```
/// #include "my_application_ui.h"
/// #include <thread>
///
/// int main(int argc, char **argv)
/// {
/// auto ui = MyApplicationUI::create();
/// ui->set_status_label("Pending");
///
/// std::thread worker_thread([ui]{
/// while (...) {
/// auto message = slint::blocking_invoke_from_event_loop([ui]() {
/// return ui->get_message();
/// }
/// do_something(message);
/// ...
/// });
/// });
/// ...
/// ui->run();
/// ...
/// }
/// ```
template<std::invocable Functor>
auto blocking_invoke_from_event_loop(Functor f) -> std::invoke_result_t<Functor>
{
std::optional<std::invoke_result_t<Functor>> result;
std::mutex mtx;
std::condition_variable cv;
invoke_from_event_loop([&] {
auto r = f();
std::unique_lock lock(mtx);
result = std::move(r);
cv.notify_one();
});
std::unique_lock lock(mtx);
cv.wait(lock, [&] { return result.has_value(); });
return std::move(*result);
}
# if !defined(DOXYGEN) // Doxygen doesn't see this as an overload of the previous one
// clang-format off
template<std::invocable Functor>
requires(std::is_void_v<std::invoke_result_t<Functor>>)
void blocking_invoke_from_event_loop(Functor f)
// clang-format on
{
std::mutex mtx;
std::condition_variable cv;
bool ok = false;
invoke_from_event_loop([&] {
f();
std::unique_lock lock(mtx);
ok = true;
cv.notify_one();
});
std::unique_lock lock(mtx);
cv.wait(lock, [&] { return ok; });
}
# endif
#endif
/// Sets the application id for use on Wayland or X11 with
/// [xdg](https://specifications.freedesktop.org/desktop-entry-spec/latest/) compliant window
/// managers. This must be set before the window is shown.
inline void set_xdg_app_id(std::string_view xdg_app_id)
{
private_api::assert_main_thread();
SharedString s = xdg_app_id;
cbindgen_private::slint_set_xdg_app_id(&s);
}
} // namespace slint