mirror of
https://github.com/slint-ui/slint.git
synced 2025-09-30 22:01:13 +00:00

Move the layout constraint tracker into the window where we can apply the constraints right before drawing, instead of doing that from within the event loop. This allows to remove the component parameter from the run function.
684 lines
24 KiB
C++
684 lines
24 KiB
C++
/* LICENSE BEGIN
|
|
This file is part of the SixtyFPS Project -- https://sixtyfps.io
|
|
Copyright (c) 2020 Olivier Goffart <olivier.goffart@sixtyfps.io>
|
|
Copyright (c) 2020 Simon Hausmann <simon.hausmann@sixtyfps.io>
|
|
|
|
SPDX-License-Identifier: GPL-3.0-only
|
|
This file is also available under commercial licensing terms.
|
|
Please contact info@sixtyfps.io for more information.
|
|
LICENSE END */
|
|
#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 <vector>
|
|
#include <memory>
|
|
#include <algorithm>
|
|
#include <iostream> // FIXME: remove: iostream always bring it lots of code so we should not have it in this header
|
|
|
|
namespace sixtyfps::cbindgen_private {
|
|
// Workaround https://github.com/eqrion/cbindgen/issues/43
|
|
struct ComponentVTable;
|
|
struct ItemVTable;
|
|
}
|
|
#include "sixtyfps_internal.h"
|
|
#include "sixtyfps_default_backend_internal.h"
|
|
#include "sixtyfps_qt_internal.h"
|
|
|
|
namespace sixtyfps {
|
|
|
|
namespace private_api {
|
|
extern "C" {
|
|
extern const cbindgen_private::ItemVTable RectangleVTable;
|
|
extern const cbindgen_private::ItemVTable BorderRectangleVTable;
|
|
extern const cbindgen_private::ItemVTable TextVTable;
|
|
extern const cbindgen_private::ItemVTable TouchAreaVTable;
|
|
extern const cbindgen_private::ItemVTable ImageVTable;
|
|
extern const cbindgen_private::ItemVTable PathVTable;
|
|
extern const cbindgen_private::ItemVTable FlickableVTable;
|
|
extern const cbindgen_private::ItemVTable WindowVTable;
|
|
extern const cbindgen_private::ItemVTable TextInputVTable;
|
|
|
|
extern const cbindgen_private::ItemVTable NativeButtonVTable;
|
|
extern const cbindgen_private::ItemVTable NativeCheckBoxVTable;
|
|
extern const cbindgen_private::ItemVTable NativeSpinBoxVTable;
|
|
extern const cbindgen_private::ItemVTable NativeSliderVTable;
|
|
extern const cbindgen_private::ItemVTable NativeGroupBoxVTable;
|
|
extern const cbindgen_private::ItemVTable NativeLineEditVTable;
|
|
extern const cbindgen_private::ItemVTable NativeScrollViewVTable;
|
|
extern const cbindgen_private::ItemVTable NativeStandardListViewItemVTable;
|
|
extern const cbindgen_private::ItemVTable NativeComboBoxVTable;
|
|
}
|
|
}
|
|
|
|
// Bring opaque structure in scope
|
|
namespace private_api {
|
|
using cbindgen_private::ComponentVTable;
|
|
using cbindgen_private::ItemVTable;
|
|
using ComponentRef = vtable::VRef<private_api::ComponentVTable>;
|
|
using ItemRef = vtable::VRef<private_api::ItemVTable>;
|
|
using ItemVisitorRefMut = vtable::VRefMut<cbindgen_private::ItemVisitorVTable>;
|
|
}
|
|
using cbindgen_private::EasingCurve;
|
|
using cbindgen_private::PropertyAnimation;
|
|
using cbindgen_private::Slice;
|
|
using cbindgen_private::TextHorizontalAlignment;
|
|
using cbindgen_private::TextVerticalAlignment;
|
|
using cbindgen_private::TraversalOrder;
|
|
|
|
namespace private_api {
|
|
using ItemTreeNode = cbindgen_private::ItemTreeNode<uint8_t>;
|
|
|
|
class ComponentWindow
|
|
{
|
|
public:
|
|
ComponentWindow() { cbindgen_private::sixtyfps_component_window_init(&inner); }
|
|
~ComponentWindow() { cbindgen_private::sixtyfps_component_window_drop(&inner); }
|
|
ComponentWindow(const ComponentWindow &) = delete;
|
|
ComponentWindow(ComponentWindow &&) = delete;
|
|
ComponentWindow &operator=(const ComponentWindow &) = delete;
|
|
|
|
template<typename Component>
|
|
void run(const Component *c) const
|
|
{
|
|
sixtyfps_component_window_run(&inner);
|
|
}
|
|
|
|
float scale_factor() const { return sixtyfps_component_window_get_scale_factor(&inner); }
|
|
void set_scale_factor(float value) const
|
|
{
|
|
sixtyfps_component_window_set_scale_factor(&inner, value);
|
|
}
|
|
|
|
template<typename Component>
|
|
void free_graphics_resources(Component *c) const
|
|
{
|
|
cbindgen_private::sixtyfps_component_window_free_graphics_resources(
|
|
&inner, vtable::VRef<ComponentVTable> { &Component::component_type, c });
|
|
}
|
|
|
|
void set_focus_item(vtable::VRef<ComponentVTable> c, vtable::VRef<ItemVTable> item)
|
|
{
|
|
cbindgen_private::sixtyfps_component_window_set_focus_item(&inner, c, item);
|
|
}
|
|
|
|
template<typename Component, typename ItemTree>
|
|
void init_items(Component *c, ItemTree items) const
|
|
{
|
|
cbindgen_private::sixtyfps_component_init_items(
|
|
vtable::VRef<ComponentVTable> { &Component::component_type, c }, items, &inner);
|
|
}
|
|
|
|
template<typename Component>
|
|
void set_component(const Component &c) const
|
|
{
|
|
auto self_rc = c.self_weak.lock().value();
|
|
sixtyfps_component_window_set_component(
|
|
&inner, reinterpret_cast<cbindgen_private::ComponentRc *>(&self_rc));
|
|
}
|
|
|
|
private:
|
|
cbindgen_private::ComponentWindowOpaque inner;
|
|
};
|
|
}
|
|
|
|
using cbindgen_private::BorderRectangle;
|
|
using cbindgen_private::Flickable;
|
|
using cbindgen_private::Image;
|
|
using cbindgen_private::Path;
|
|
using cbindgen_private::Rectangle;
|
|
using cbindgen_private::Text;
|
|
using cbindgen_private::TextInput;
|
|
using cbindgen_private::TouchArea;
|
|
using cbindgen_private::Window;
|
|
|
|
using cbindgen_private::NativeButton;
|
|
using cbindgen_private::NativeCheckBox;
|
|
using cbindgen_private::NativeComboBox;
|
|
using cbindgen_private::NativeGroupBox;
|
|
using cbindgen_private::NativeLineEdit;
|
|
using cbindgen_private::NativeScrollView;
|
|
using cbindgen_private::NativeSlider;
|
|
using cbindgen_private::NativeSpinBox;
|
|
using cbindgen_private::NativeStandardListViewItem;
|
|
using cbindgen_private::NativeStyleMetrics;
|
|
|
|
namespace private_api {
|
|
constexpr inline ItemTreeNode make_item_node(std::uintptr_t offset,
|
|
const cbindgen_private::ItemVTable *vtable,
|
|
uint32_t child_count, uint32_t child_index)
|
|
{
|
|
return ItemTreeNode { ItemTreeNode::Item_Body {
|
|
ItemTreeNode::Tag::Item, { vtable, offset }, child_count, child_index } };
|
|
}
|
|
|
|
constexpr inline ItemTreeNode make_dyn_node(std::uintptr_t offset)
|
|
{
|
|
return ItemTreeNode { ItemTreeNode::DynamicTree_Body { ItemTreeNode::Tag::DynamicTree,
|
|
offset } };
|
|
}
|
|
|
|
inline ItemRef get_item_ref(ComponentRef component, Slice<ItemTreeNode> item_tree, int index)
|
|
{
|
|
const auto &item = item_tree.ptr[index].item.item;
|
|
return ItemRef { item.vtable, reinterpret_cast<char *>(component.instance) + item.offset };
|
|
}
|
|
|
|
}
|
|
|
|
using cbindgen_private::FocusEvent;
|
|
using cbindgen_private::FocusEventResult;
|
|
using cbindgen_private::InputEventResult;
|
|
using cbindgen_private::KeyEvent;
|
|
using cbindgen_private::KeyEventResult;
|
|
using cbindgen_private::MouseEvent;
|
|
using cbindgen_private::sixtyfps_visit_item_tree;
|
|
namespace private_api {
|
|
template<typename GetDynamic>
|
|
inline InputEventResult process_input_event(ComponentRef component, int64_t &mouse_grabber,
|
|
MouseEvent mouse_event, Slice<ItemTreeNode> tree,
|
|
GetDynamic get_dynamic, const ComponentWindow *window,
|
|
const ComponentRef *app_component)
|
|
{
|
|
if (mouse_grabber != -1) {
|
|
auto item_index = mouse_grabber & 0xffffffff;
|
|
auto rep_index = mouse_grabber >> 32;
|
|
auto offset = cbindgen_private::sixtyfps_item_offset(component, tree, item_index);
|
|
mouse_event.pos = { mouse_event.pos.x - offset.x, mouse_event.pos.y - offset.y };
|
|
const auto &item_node = tree.ptr[item_index];
|
|
InputEventResult result = InputEventResult::EventIgnored;
|
|
switch (item_node.tag) {
|
|
case ItemTreeNode::Tag::Item:
|
|
result = item_node.item.item.vtable->input_event(
|
|
{
|
|
item_node.item.item.vtable,
|
|
reinterpret_cast<char *>(component.instance)
|
|
+ item_node.item.item.offset,
|
|
},
|
|
mouse_event, window, *app_component);
|
|
break;
|
|
case ItemTreeNode::Tag::DynamicTree: {
|
|
ComponentRef comp = get_dynamic(item_node.dynamic_tree.index, rep_index);
|
|
result = comp.vtable->input_event(comp, mouse_event, window, app_component);
|
|
} break;
|
|
}
|
|
if (result != InputEventResult::GrabMouse) {
|
|
mouse_grabber = -1;
|
|
}
|
|
return result;
|
|
} else {
|
|
return cbindgen_private::sixtyfps_process_ungrabbed_mouse_event(
|
|
component, mouse_event, window, *app_component, &mouse_grabber);
|
|
}
|
|
}
|
|
template<typename GetDynamic>
|
|
inline KeyEventResult process_key_event(ComponentRef component, int64_t focus_item,
|
|
const KeyEvent *event, Slice<ItemTreeNode> tree,
|
|
GetDynamic get_dynamic, const ComponentWindow *window)
|
|
{
|
|
if (focus_item != -1) {
|
|
auto item_index = focus_item & 0xffffffff;
|
|
auto rep_index = focus_item >> 32;
|
|
const auto &item_node = tree.ptr[item_index];
|
|
switch (item_node.tag) {
|
|
case ItemTreeNode::Tag::Item:
|
|
return item_node.item.item.vtable->key_event(
|
|
{
|
|
item_node.item.item.vtable,
|
|
reinterpret_cast<char *>(component.instance)
|
|
+ item_node.item.item.offset,
|
|
},
|
|
event, window);
|
|
case ItemTreeNode::Tag::DynamicTree: {
|
|
ComponentRef comp = get_dynamic(item_node.dynamic_tree.index, rep_index);
|
|
return comp.vtable->key_event(comp, event, window);
|
|
};
|
|
}
|
|
}
|
|
return KeyEventResult::EventIgnored;
|
|
}
|
|
|
|
template<typename GetDynamic>
|
|
inline FocusEventResult process_focus_event(ComponentRef component, int64_t &focus_item,
|
|
const FocusEvent *event, Slice<ItemTreeNode> tree,
|
|
GetDynamic get_dynamic, const ComponentWindow *window)
|
|
{
|
|
switch (event->tag) {
|
|
case FocusEvent::Tag::FocusIn:
|
|
return cbindgen_private::sixtyfps_locate_and_activate_focus_item(component, event, window,
|
|
&focus_item);
|
|
case FocusEvent::Tag::FocusOut:
|
|
[[fallthrough]];
|
|
case FocusEvent::Tag::WindowReceivedFocus:
|
|
[[fallthrough]];
|
|
case FocusEvent::Tag::WindowLostFocus:
|
|
if (focus_item != -1) {
|
|
auto item_index = focus_item & 0xffffffff;
|
|
auto rep_index = focus_item >> 32;
|
|
const auto &item_node = tree.ptr[item_index];
|
|
switch (item_node.tag) {
|
|
case ItemTreeNode::Tag::Item:
|
|
item_node.item.item.vtable->focus_event(
|
|
{
|
|
item_node.item.item.vtable,
|
|
reinterpret_cast<char *>(component.instance)
|
|
+ item_node.item.item.offset,
|
|
},
|
|
event, window);
|
|
break;
|
|
case ItemTreeNode::Tag::DynamicTree: {
|
|
ComponentRef comp = get_dynamic(item_node.dynamic_tree.index, rep_index);
|
|
comp.vtable->focus_event(comp, event, window);
|
|
} break;
|
|
}
|
|
if (event->tag == FocusEvent::Tag::FocusOut) {
|
|
focus_item = -1;
|
|
}
|
|
return FocusEventResult::FocusItemFound;
|
|
} else {
|
|
return FocusEventResult::FocusItemNotFound;
|
|
}
|
|
}
|
|
return FocusEventResult::FocusItemNotFound;
|
|
}
|
|
|
|
void dealloc(const ComponentVTable *, uint8_t *ptr, vtable::Layout layout)
|
|
{
|
|
#ifdef __cpp_sized_deallocation
|
|
::operator delete(reinterpret_cast<void *>(ptr), layout.size,
|
|
static_cast<std::align_val_t>(layout.align));
|
|
#else
|
|
::operator delete(reinterpret_cast<void *>(ptr), static_cast<std::align_val_t>(layout.align));
|
|
#endif
|
|
}
|
|
|
|
template<typename T>
|
|
vtable::Layout drop_in_place(ComponentRef component)
|
|
{
|
|
reinterpret_cast<T *>(component.instance)->~T();
|
|
return vtable::Layout { sizeof(T), alignof(T) };
|
|
}
|
|
} // namespace private_api
|
|
|
|
template<typename T>
|
|
class ComponentWeakHandle;
|
|
|
|
/// The component handle is like a shared pointer to a component in the generated code.
|
|
/// In order to get a component, use `T::create()` where T is the name of the component
|
|
/// in the .60 file. This give you a `ComponentHandle<T>`
|
|
template<typename T>
|
|
class ComponentHandle
|
|
{
|
|
vtable::VRc<private_api::ComponentVTable, T> inner;
|
|
friend class ComponentWeakHandle<T>;
|
|
|
|
public:
|
|
/// internal constructor
|
|
ComponentHandle(const vtable::VRc<private_api::ComponentVTable, T> &inner) : inner(inner) { }
|
|
|
|
const T *operator->() const { return inner.operator->(); }
|
|
const T &operator*() const { return inner.operator*(); }
|
|
};
|
|
|
|
/// A weak reference to the component. Can be constructed from a `ComponentHandle<T>`
|
|
template<typename T>
|
|
class ComponentWeakHandle
|
|
{
|
|
vtable::VWeak<private_api::ComponentVTable, T> inner;
|
|
|
|
public:
|
|
ComponentWeakHandle() = default;
|
|
ComponentWeakHandle(const ComponentHandle<T> &other) : inner(other.inner) { }
|
|
std::optional<ComponentHandle<T>> lock() const
|
|
{
|
|
if (auto l = inner.lock()) {
|
|
return { ComponentHandle(*l) };
|
|
} else {
|
|
return {};
|
|
}
|
|
}
|
|
};
|
|
|
|
// layouts:
|
|
using cbindgen_private::box_layout_info;
|
|
using cbindgen_private::BoxLayoutCellData;
|
|
using cbindgen_private::BoxLayoutData;
|
|
using cbindgen_private::grid_layout_info;
|
|
using cbindgen_private::GridLayoutCellData;
|
|
using cbindgen_private::GridLayoutData;
|
|
using cbindgen_private::LayoutAlignment;
|
|
using cbindgen_private::LayoutInfo;
|
|
using cbindgen_private::Padding;
|
|
using cbindgen_private::PathLayoutData;
|
|
using cbindgen_private::PathLayoutItemData;
|
|
using cbindgen_private::Rect;
|
|
using cbindgen_private::solve_box_layout;
|
|
using cbindgen_private::solve_grid_layout;
|
|
using cbindgen_private::solve_path_layout;
|
|
|
|
inline LayoutInfo LayoutInfo::merge(const LayoutInfo &other) const
|
|
{
|
|
// Note: This "logic" is duplicated from LayoutInfo::merge in layout.rs.
|
|
return LayoutInfo { std::max(min_width, other.min_width),
|
|
std::min(max_width, other.max_width),
|
|
std::max(min_height, other.min_height),
|
|
std::min(max_height, other.max_height),
|
|
std::min(horizontal_stretch, other.horizontal_stretch),
|
|
std::min(vertical_stretch, other.vertical_stretch) };
|
|
}
|
|
|
|
// models
|
|
struct AbstractRepeaterView
|
|
{
|
|
~AbstractRepeaterView() = default;
|
|
virtual void row_added(int index, int count) = 0;
|
|
virtual void row_removed(int index, int count) = 0;
|
|
virtual void row_changed(int index) = 0;
|
|
};
|
|
using ModelPeer = std::weak_ptr<AbstractRepeaterView>;
|
|
|
|
template<typename ModelData>
|
|
class Model
|
|
{
|
|
public:
|
|
virtual ~Model() = default;
|
|
Model() = default;
|
|
Model(const Model &) = delete;
|
|
Model &operator=(const Model &) = delete;
|
|
|
|
/// The amount of row in the model
|
|
virtual int row_count() const = 0;
|
|
/// Returns the data for a particular row. This function should be called with `row <
|
|
/// row_count()`.
|
|
virtual ModelData row_data(int i) const = 0;
|
|
/// Sets the data for a particular row. This function should be called with `row < row_count()`.
|
|
/// If the model cannot support data changes, then it is ok to do nothing (default
|
|
/// implementation). If the model can update the data, the implmentation should also call
|
|
/// row_changed.
|
|
virtual void set_row_data(int, const ModelData &) {};
|
|
|
|
/// Internal function called by the view to register itself
|
|
void attach_peer(ModelPeer p) { peers.push_back(std::move(p)); }
|
|
|
|
protected:
|
|
/// Notify the views that a specific row was changed
|
|
void row_changed(int row)
|
|
{
|
|
for_each_peers([=](auto peer) { peer->row_changed(row); });
|
|
}
|
|
/// Notify the views that rows were added
|
|
void row_added(int index, int count)
|
|
{
|
|
for_each_peers([=](auto peer) { peer->row_added(index, count); });
|
|
}
|
|
/// Notify the views that rows were removed
|
|
void row_removed(int index, int count)
|
|
{
|
|
for_each_peers([=](auto peer) { peer->row_removed(index, count); });
|
|
}
|
|
|
|
private:
|
|
template<typename F>
|
|
void for_each_peers(const F &f)
|
|
{
|
|
peers.erase(std::remove_if(peers.begin(), peers.end(),
|
|
[&](const auto &p) {
|
|
if (auto pp = p.lock()) {
|
|
f(pp);
|
|
return false;
|
|
}
|
|
return true;
|
|
}),
|
|
peers.end());
|
|
}
|
|
std::vector<ModelPeer> peers;
|
|
};
|
|
|
|
/// A Model backed by an array of constant size
|
|
template<int Count, typename ModelData>
|
|
class ArrayModel : public Model<ModelData>
|
|
{
|
|
std::array<ModelData, Count> data;
|
|
|
|
public:
|
|
template<typename... A>
|
|
ArrayModel(A &&... a) : data { std::forward<A>(a)... }
|
|
{
|
|
}
|
|
int row_count() const override { return Count; }
|
|
ModelData row_data(int i) const override { return data[i]; }
|
|
void set_row_data(int i, const ModelData &value) override
|
|
{
|
|
data[i] = value;
|
|
this->row_changed(i);
|
|
}
|
|
};
|
|
|
|
/// Model to be used when we just want to repeat without data.
|
|
struct IntModel : Model<int>
|
|
{
|
|
IntModel(int d) : data(d) { }
|
|
int data;
|
|
int row_count() const override { return data; }
|
|
int row_data(int value) const override { return value; }
|
|
};
|
|
|
|
/// A Model backed by a SharedArray
|
|
template<typename ModelData>
|
|
class VectorModel : public Model<ModelData>
|
|
{
|
|
std::vector<ModelData> data;
|
|
|
|
public:
|
|
VectorModel() = default;
|
|
VectorModel(std::vector<ModelData> array) : data(std::move(array)) { }
|
|
int row_count() const override { return data.size(); }
|
|
ModelData row_data(int i) const override { return data[i]; }
|
|
void set_row_data(int i, const ModelData &value) override
|
|
{
|
|
data[i] = value;
|
|
this->row_changed(i);
|
|
}
|
|
|
|
/// Append a new row with the given value
|
|
void push_back(const ModelData &value)
|
|
{
|
|
data.push_back(value);
|
|
this->row_added(data.size() - 1, 1);
|
|
}
|
|
|
|
/// Remove the row at the given index from the model
|
|
void erase(int index)
|
|
{
|
|
data.erase(data.begin() + index);
|
|
this->row_removed(index, 1);
|
|
}
|
|
};
|
|
|
|
template<typename C, typename ModelData>
|
|
class Repeater
|
|
{
|
|
Property<std::shared_ptr<Model<ModelData>>> model;
|
|
|
|
struct RepeaterInner : AbstractRepeaterView
|
|
{
|
|
enum class State { Clean, Dirty };
|
|
struct ComponentWithState
|
|
{
|
|
State state = State::Dirty;
|
|
std::optional<vtable::VRc<private_api::ComponentVTable, C>> ptr;
|
|
};
|
|
std::vector<ComponentWithState> data;
|
|
bool is_dirty = true;
|
|
|
|
void row_added(int index, int count) override
|
|
{
|
|
is_dirty = true;
|
|
data.resize(data.size() + count);
|
|
std::rotate(data.begin() + index, data.end() - count, data.end());
|
|
}
|
|
void row_changed(int index) override
|
|
{
|
|
is_dirty = true;
|
|
data[index].state = State::Dirty;
|
|
}
|
|
void row_removed(int index, int count) override
|
|
{
|
|
is_dirty = true;
|
|
data.erase(data.begin() + index, data.begin() + index + count);
|
|
for (std::size_t i = index; i < data.size(); ++i) {
|
|
// all the indexes are dirty
|
|
data[i].state = State::Dirty;
|
|
}
|
|
}
|
|
};
|
|
|
|
public:
|
|
// FIXME: should be private, but layouting code uses it.
|
|
mutable std::shared_ptr<RepeaterInner> inner;
|
|
|
|
template<typename F>
|
|
void set_model_binding(F &&binding) const
|
|
{
|
|
model.set_binding(std::forward<F>(binding));
|
|
}
|
|
|
|
template<typename Parent>
|
|
void ensure_updated(const Parent *parent) const
|
|
{
|
|
if (model.is_dirty()) {
|
|
inner = std::make_shared<RepeaterInner>();
|
|
if (auto m = model.get()) {
|
|
m->attach_peer(inner);
|
|
}
|
|
}
|
|
|
|
if (inner && inner->is_dirty) {
|
|
inner->is_dirty = false;
|
|
if (auto m = model.get()) {
|
|
int count = m->row_count();
|
|
inner->data.resize(count);
|
|
for (int i = 0; i < count; ++i) {
|
|
auto &c = inner->data[i];
|
|
if (!c.ptr) {
|
|
c.ptr = { vtable::VRc<private_api::ComponentVTable, C>::make(parent) };
|
|
}
|
|
if (c.state == RepeaterInner::State::Dirty) {
|
|
(*c.ptr)->update_data(i, m->row_data(i));
|
|
}
|
|
}
|
|
} else {
|
|
inner->data.clear();
|
|
}
|
|
}
|
|
}
|
|
|
|
template<typename Parent>
|
|
void ensure_updated_listview(const Parent *parent, const Property<float> *viewport_width,
|
|
const Property<float> *viewport_height,
|
|
[[maybe_unused]] const Property<float> *viewport_y,
|
|
float listview_width, [[maybe_unused]] float listview_height) const
|
|
{
|
|
// TODO: the rust code in model.rs try to only allocate as many items as visible items
|
|
ensure_updated(parent);
|
|
|
|
float h = compute_layout_listview(viewport_width, listview_width);
|
|
viewport_height->set(h);
|
|
}
|
|
|
|
intptr_t visit(TraversalOrder order, private_api::ItemVisitorRefMut visitor) const
|
|
{
|
|
for (std::size_t i = 0; i < inner->data.size(); ++i) {
|
|
int index = order == TraversalOrder::BackToFront ? i : inner->data.size() - 1 - i;
|
|
auto ref = item_at(index);
|
|
if (ref.vtable->visit_children_item(ref, -1, order, visitor) != -1) {
|
|
return index;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
vtable::VRef<private_api::ComponentVTable> item_at(int i) const
|
|
{
|
|
const auto &x = inner->data.at(i);
|
|
return { &C::component_type, const_cast<C *>(&(**x.ptr)) };
|
|
}
|
|
|
|
void compute_layout(cbindgen_private::Rect parent_rect) const
|
|
{
|
|
if (!inner)
|
|
return;
|
|
for (auto &x : inner->data) {
|
|
(*x.ptr)->apply_layout({ &C::component_type, const_cast<C *>(&(**x.ptr)) },
|
|
parent_rect);
|
|
}
|
|
}
|
|
|
|
float compute_layout_listview(const Property<float> *viewport_width, float listview_width) const
|
|
{
|
|
float offset = 0;
|
|
viewport_width->set(listview_width);
|
|
if (!inner)
|
|
return offset;
|
|
for (auto &x : inner->data) {
|
|
(*x.ptr)->listview_layout(&offset, viewport_width);
|
|
}
|
|
return offset;
|
|
}
|
|
|
|
void model_set_row_data(int row, const ModelData &data) const
|
|
{
|
|
if (model.is_dirty()) {
|
|
std::abort();
|
|
}
|
|
if (auto m = model.get()) {
|
|
m->set_row_data(row, data);
|
|
if (inner && inner->is_dirty) {
|
|
auto &c = inner->data[row];
|
|
if (c.state == RepeaterInner::State::Dirty && c.ptr) {
|
|
(*c.ptr)->update_data(row, m->row_data(row));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
Flickable::Flickable()
|
|
{
|
|
sixtyfps_flickable_data_init(&data);
|
|
}
|
|
Flickable::~Flickable()
|
|
{
|
|
sixtyfps_flickable_data_free(&data);
|
|
}
|
|
|
|
NativeStyleMetrics::NativeStyleMetrics()
|
|
{
|
|
sixtyfps_init_native_style_metrics(this);
|
|
}
|
|
|
|
using cbindgen_private::StandardListViewItem;
|
|
namespace cbindgen_private {
|
|
bool operator==(const StandardListViewItem &a, const StandardListViewItem &b)
|
|
{
|
|
static_assert(sizeof(StandardListViewItem) == sizeof(std::tuple<SharedString>),
|
|
"must update to cover all fields");
|
|
return a.text == b.text;
|
|
}
|
|
bool operator!=(const StandardListViewItem &a, const StandardListViewItem &b)
|
|
{
|
|
return !(a == b);
|
|
}
|
|
}
|
|
|
|
namespace private_api {
|
|
template<int Major, int Minor, int Patch>
|
|
struct VersionCheckHelper
|
|
{
|
|
};
|
|
}
|
|
|
|
} // namespace sixtyfps
|