mirror of
https://github.com/slint-ui/slint.git
synced 2025-07-07 21:25:33 +00:00
500 lines
18 KiB
C++
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
|