Created
February 7, 2016 02:55
-
-
Save anonymous/c8f633788ddaeb24c291 to your computer and use it in GitHub Desktop.
Ugly code by Prismatic
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
Copyright (C) 2016 Preet Desai (preet.desai@gmail.com) | |
Licensed under the Apache License, Version 2.0 (the "License"); | |
you may not use this file except in compliance with the License. | |
You may obtain a copy of the License at | |
http://www.apache.org/licenses/LICENSE-2.0 | |
Unless required by applicable law or agreed to in writing, software | |
distributed under the License is distributed on an "AS IS" BASIS, | |
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
See the License for the specific language governing permissions and | |
limitations under the License. | |
*/ | |
#include <raintk/test/RainTkTestContext.hpp> | |
#include <raintk/RainTkListModelSTLVector.hpp> | |
#include <raintk/RainTkListDelegate.hpp> | |
#include <raintk/RainTkScrollArea.hpp> | |
#include <raintk/RainTkRectangle.hpp> | |
// =========================================================== // | |
// =========================================================== // | |
namespace raintk | |
{ | |
struct TestItem | |
{ | |
glm::u8vec3 color; | |
}; | |
class TestDelegate : public ListDelegate | |
{ | |
public: | |
TestDelegate(ks::Object::Key const &key, | |
shared_ptr<Widget> parent) : | |
ListDelegate(key,parent), | |
m_index(0) | |
{} | |
void Init(ks::Object::Key const &, | |
shared_ptr<TestDelegate> const &this_delegate) | |
{ | |
width = 5*mm; | |
height = 10*mm; // make height random | |
m_rect = ks::make_object<Rectangle>(this_delegate,""); | |
m_rect->width = [this](){ return width.Get(); }; | |
m_rect->height = [this](){ return height.Get(); }; | |
} | |
~TestDelegate() | |
{ | |
} | |
void SetIndex(uint index) | |
{ | |
m_index = index; | |
width = (5+m_index)*mm; | |
} | |
uint GetIndex() const | |
{ | |
return m_index; | |
} | |
void SetData(TestItem const &item) | |
{ | |
m_color = item.color; | |
m_rect->color = m_color; | |
} | |
private: | |
uint m_index; | |
shared_ptr<Rectangle> m_rect; | |
glm::vec3 m_color; | |
}; | |
// =========================================================== // | |
class ListViewDelegateHeightInvalid : public ks::Exception | |
{ | |
public: | |
ListViewDelegateHeightInvalid(std::string msg) : | |
ks::Exception(ks::Exception::ErrorLevel::ERROR,msg) | |
{} | |
~ListViewDelegateHeightInvalid() = default; | |
}; | |
class ListViewTODO : public ks::Exception | |
{ | |
public: | |
ListViewTODO() : | |
ks::Exception(ks::Exception::ErrorLevel::ERROR,"") | |
{} | |
~ListViewTODO() = default; | |
}; | |
// =========================================================== // | |
struct ListViewProperties | |
{ | |
enum class Layout | |
{ | |
Row, | |
Column | |
}; | |
enum class Order | |
{ | |
Ascending, | |
Descending | |
}; | |
}; | |
// =========================================================== // | |
// =========================================================== // | |
// =========================================================== // | |
template<typename ItemType, typename DelegateType> | |
class ListView : public raintk::ScrollArea | |
{ | |
using ListViewType = ListView<ItemType,DelegateType>; | |
using DelegateList = | |
std::vector<shared_ptr<DelegateType>>; | |
using DelegateRange = | |
std::pair< | |
typename DelegateList::iterator, | |
typename DelegateList::iterator | |
>; | |
shared_ptr<ListModel<ItemType>> m_list_model; | |
// * Connections | |
Id m_cid_content_x_changed; | |
Id m_cid_content_y_changed; | |
Id m_cid_before_adding_items; | |
Id m_cid_added_items; | |
Id m_cid_before_removing_items; | |
Id m_cid_removed_items; | |
Id m_cid_data_changed; | |
Id m_cid_layout_changed; | |
// * Delegate bounds | |
DynamicPropertyPullNotify<float> m_delegate_bounds_top{ | |
name+".delegate_bounds_top",0.0f | |
}; | |
DynamicPropertyPullNotify<float> m_delegate_bounds_bottom{ | |
name+".delegate_bounds_bottom",0.0f | |
}; | |
DynamicPropertyPullNotify<float> m_delegate_bounds_left{ | |
name+".delegate_bounds_left",0.0f | |
}; | |
DynamicPropertyPullNotify<float> m_delegate_bounds_right{ | |
name+".delegate_bounds_right",0.0f | |
}; | |
// These properties point to the four above properties | |
// depending on the layout direction and order | |
DynamicPropertyPullNotify<float>* m_delegate_bounds_start; | |
DynamicPropertyPullNotify<float>* m_delegate_bounds_end; | |
DynamicPropertyPullNotify<float>* m_view_size; | |
DynamicPropertyPullNotify<float>* m_content_size; | |
DynamicPropertyPullNotify<float>* m_content_position; | |
// * The average height or width of a delegate, used | |
// to estimate the dimensions of the content_parent | |
// and place the first delegate | |
float m_average_delegate_size{0}; | |
float m_minimum_delegate_size{1.0f*mm}; // TODO setter/getter | |
uint m_max_delegate_count{30}; // TODO setter/getter | |
std::function<float(shared_ptr<DelegateType> const &)> m_get_delegate_size; | |
std::function<float(shared_ptr<DelegateType> const &)> m_get_delegate_position; | |
std::function<void(shared_ptr<DelegateType> const &,float)> m_set_delegate_position; | |
DelegateList m_list_delegates; | |
// * Guidelines to help debug, should be disabled | |
// in release builds | |
using ListRectangles = std::vector<shared_ptr<Rectangle>>; | |
ListRectangles m_list_guidelines; | |
public: | |
// Properties | |
DynamicPropertyPullNotify<float> delegate_extents{ | |
name+"delegate_extents",25*mm | |
}; | |
DynamicPropertyPullNotify<float> spacing{ | |
name+".spacing",0.0f | |
}; | |
DynamicPropertyPullNotify<ListViewProperties::Layout> layout{ | |
name+".layout",ListViewProperties::Layout::Column | |
}; | |
// === // | |
using base_type = raintk::ScrollArea; | |
ListView(ks::Object::Key const &key, | |
shared_ptr<Widget> parent, | |
std::string name) : | |
raintk::ScrollArea(key,parent,std::move(name)) | |
{} | |
~ListView() | |
{} | |
void Init(ks::Object::Key const &, | |
shared_ptr<ListViewType> const &this_view) | |
{ | |
setupLayoutDirection(); | |
// Setup connections | |
// Setup a slot for both content x and y changed | |
// signals so we don't have to change them when | |
// the layout direction changes -- the ScrollArea | |
// should lock respective directions and prevent | |
// unused signals anyway | |
m_cid_content_y_changed = | |
m_content_parent->y.signal_changed.Connect( | |
this_view, | |
&ListViewType::onScrollPositionChanged, | |
ks::ConnectionType::Direct); | |
m_cid_content_x_changed = | |
m_content_parent->x.signal_changed.Connect( | |
this_view, | |
&ListViewType::onScrollPositionChanged, | |
ks::ConnectionType::Direct); | |
layout.signal_changed.Connect( | |
this_view, | |
&ListViewType::onLayoutDirectionChanged, | |
ks::ConnectionType::Direct); | |
} | |
void SetListModel(shared_ptr<ListModel<ItemType>> list_model) | |
{ | |
// Remove all previous delegates | |
for(auto& delegate : m_list_delegates) | |
{ | |
this->RemoveChild(delegate); | |
} | |
m_list_delegates.clear(); | |
// Disconnect previous connections | |
if(m_list_model) | |
{ | |
m_list_model->signal_before_adding_items.Disconnect( | |
m_cid_before_adding_items); | |
m_list_model->signal_added_items.Disconnect( | |
m_cid_added_items); | |
m_list_model->signal_before_removing_items.Disconnect( | |
m_cid_before_removing_items); | |
m_list_model->signal_removed_items.Disconnect( | |
m_cid_removed_items); | |
m_list_model->signal_data_changed.Disconnect( | |
m_cid_data_changed); | |
m_list_model->signal_layout_changed.Disconnect( | |
m_cid_layout_changed); | |
} | |
// Setup new connections | |
m_list_model = list_model; | |
shared_ptr<ListViewType> this_view = | |
std::static_pointer_cast<ListViewType>( | |
shared_from_this()); | |
m_cid_before_adding_items = | |
m_list_model->signal_before_adding_items.Connect( | |
this_view, | |
&ListViewType::onBeforeAddingItems, | |
ks::ConnectionType::Direct); | |
m_cid_added_items = m_list_model->signal_added_items.Connect( | |
this_view, | |
&ListViewType::onAddedItems, | |
ks::ConnectionType::Direct); | |
m_cid_before_removing_items = | |
m_list_model->signal_before_removing_items.Connect( | |
this_view, | |
&ListViewType::onBeforeRemovingItems, | |
ks::ConnectionType::Direct); | |
m_cid_removed_items = | |
m_list_model->signal_removed_items.Connect( | |
this_view, | |
&ListViewType::onRemovedItems, | |
ks::ConnectionType::Direct); | |
m_cid_data_changed = | |
m_list_model->signal_data_changed.Connect( | |
this_view, | |
&ListViewType::onDataChanged, | |
ks::ConnectionType::Direct); | |
m_cid_layout_changed = | |
m_list_model->signal_layout_changed.Connect( | |
this_view, | |
&ListViewType::onLayoutChanged, | |
ks::ConnectionType::Direct); | |
m_content_parent->x = 0; | |
m_content_parent->y = 0; | |
m_content_parent->width = width.Get(); | |
m_content_parent->height = height.Get(); | |
m_cmlist_update_data->GetComponent(m_entity_id). | |
update |= UpdateData::UpdateWidget; | |
} | |
private: | |
void onBeforeAddingItems(uint idx_before, | |
uint count) | |
{ | |
(void)idx_before; | |
(void)count; | |
} | |
void onAddedItems(uint idx_first_added, | |
uint idx_end_added) | |
{ | |
(void)idx_first_added; | |
(void)idx_end_added; | |
} | |
void onBeforeRemovingItems(uint idx_first_remove, | |
uint idx_end_remove) | |
{ | |
(void)idx_first_remove; | |
(void)idx_end_remove; | |
} | |
void onRemovedItems(uint idx_after_removed, | |
uint count) | |
{ | |
(void)idx_after_removed; | |
(void)count; | |
} | |
void onDelegateDimChanged() | |
{ | |
// Mark this widget as updated | |
m_cmlist_update_data->GetComponent(m_entity_id). | |
update |= UpdateData::UpdateWidget; | |
} | |
void onDataChanged(uint) | |
{ | |
} | |
void onLayoutChanged() | |
{ | |
} | |
void setupLayoutDirection() | |
{ | |
if(layout.Get()==ListViewProperties::Layout::Column) | |
{ | |
m_view_size = &(height); | |
m_content_size = &(m_content_parent->height); | |
m_content_position = &(m_content_parent->y); | |
m_delegate_bounds_start = &(m_delegate_bounds_top); | |
m_delegate_bounds_end = &(m_delegate_bounds_bottom); | |
m_get_delegate_size = | |
[](shared_ptr<DelegateType> const &d) -> float { | |
return d->height.Get(); | |
}; | |
m_get_delegate_position = | |
[](shared_ptr<DelegateType> const &d) -> float { | |
return d->y.Get(); | |
}; | |
m_set_delegate_position = | |
[](shared_ptr<DelegateType> const &d, float new_position) { | |
return d->y = new_position; | |
}; | |
} | |
else | |
{ | |
m_view_size = &(width); | |
m_content_size = &(m_content_parent->width); | |
m_content_position = &(m_content_parent->x); | |
m_delegate_bounds_start = &(m_delegate_bounds_left); | |
m_delegate_bounds_end = &(m_delegate_bounds_right); | |
m_get_delegate_size = | |
[](shared_ptr<DelegateType> const &d) -> float { | |
return d->width.Get(); | |
}; | |
m_get_delegate_position = | |
[](shared_ptr<DelegateType> const &d) -> float { | |
return d->x.Get(); | |
}; | |
m_set_delegate_position = | |
[](shared_ptr<DelegateType> const &d, float new_position) { | |
return d->x = new_position; | |
}; | |
} | |
calcDelegateBounds(); | |
createDebugGuidelines(); | |
} | |
void onLayoutDirectionChanged() | |
{ | |
setupLayoutDirection(); | |
} | |
void onScrollPositionChanged() | |
{ | |
m_cmlist_update_data->GetComponent(m_entity_id). | |
update |= UpdateData::UpdateWidget; | |
} | |
void update() | |
{ | |
if(m_list_model==nullptr || m_list_model->GetSize()==0) | |
{ | |
return; | |
} | |
// Erase all delegates outside of the delegate extents | |
eraseDelegatesOutsideExtents( | |
m_delegate_bounds_top.Get(), | |
m_delegate_bounds_bottom.Get(), | |
m_delegate_bounds_left.Get(), | |
m_delegate_bounds_right.Get()); | |
if(m_list_delegates.empty()) | |
{ | |
// Create the first delegate using an estimated | |
// index based on the current position of the list | |
calcAverageDelegateSize(); | |
updateContentParentSize(); | |
// Clamp the current position to be within the | |
// content size | |
clampContentPositionToSize(); | |
// Get model index based on content position | |
float estimated_model_index = | |
(m_content_position->Get()*-1.0f)/ | |
(m_average_delegate_size+spacing.Get()); | |
// Create delegate for model index | |
m_list_delegates.push_back( | |
ks::make_object<DelegateType>( | |
m_content_parent)); | |
m_list_delegates.back()->SetData( | |
m_list_model->GetData( | |
estimated_model_index)); | |
m_list_delegates.back()->SetIndex( | |
estimated_model_index); | |
m_set_delegate_position( | |
m_list_delegates.back(), | |
m_content_position->Get()); | |
} | |
// There must be at least one delegate at this point | |
fillSpaceBefore(); | |
fillSpaceAfter(); | |
correctDelegatePositions(); | |
calcAverageDelegateSize(); | |
updateContentParentSize(); | |
} | |
// Fill available space before the first delegate | |
// in list_delegates | |
void fillSpaceBefore() | |
{ | |
sint model_index = m_list_delegates.front()->GetIndex(); | |
model_index--; | |
// ie. The edge closest towards the start | |
// of the delegate bounds | |
float first_start_position = | |
m_get_delegate_position( | |
m_list_delegates.front()); | |
float space_before = | |
first_start_position-m_delegate_bounds_start->Get(); | |
while((space_before > 0) && | |
(model_index >= 0) && | |
(m_list_delegates.size() <= m_max_delegate_count)) | |
{ | |
auto delegate = | |
ks::make_object<DelegateType>( | |
m_content_parent); | |
delegate->SetData(m_list_model->GetData(model_index)); | |
delegate->SetIndex(model_index); | |
m_list_delegates.insert( | |
m_list_delegates.begin(), | |
delegate); | |
first_start_position -= (spacing.Get()+m_get_delegate_size(delegate)); | |
m_set_delegate_position(delegate,first_start_position); | |
space_before = first_start_position-m_delegate_bounds_start->Get(); | |
model_index--; | |
} | |
} | |
// Fill available space after the last delegate | |
// in list_delegates | |
void fillSpaceAfter() | |
{ | |
uint model_index = m_list_delegates.back()->GetIndex(); | |
model_index++; | |
// ie. The edge closest towards the end of | |
// the delegate bounds | |
float last_end_position = | |
m_get_delegate_position( | |
m_list_delegates.back())+ | |
m_get_delegate_size( | |
m_list_delegates.back()); | |
float space_after = | |
m_delegate_bounds_end->Get()-last_end_position; | |
while((space_after > 0) && | |
(model_index < m_list_model->GetSize()) && | |
(m_list_delegates.size() <= m_max_delegate_count)) | |
{ | |
auto delegate = | |
ks::make_object<DelegateType>( | |
m_content_parent); | |
delegate->SetData(m_list_model->GetData(model_index)); | |
delegate->SetIndex(model_index); | |
m_list_delegates.push_back(delegate); | |
last_end_position += spacing.Get(); | |
m_set_delegate_position(delegate,last_end_position); | |
last_end_position += m_get_delegate_size(delegate); | |
space_after = m_delegate_bounds_end->Get()-last_end_position; | |
model_index++; | |
} | |
} | |
void correctDelegatePositions() | |
{ | |
// If the first delegate isn't currently instantiated, | |
// we don't need to correct positions | |
if(m_list_delegates.front()->GetIndex() != 0 || | |
m_get_delegate_position(m_list_delegates.front())==0.0f) | |
{ | |
return; | |
} | |
// Shift all the delegates along to the correct position | |
float position_shift = | |
m_get_delegate_position( | |
m_list_delegates.front())*-1.0f; | |
for(auto& delegate : m_list_delegates) | |
{ | |
float new_position = | |
m_get_delegate_position(delegate)+ | |
position_shift; | |
m_set_delegate_position( | |
delegate,new_position); | |
} | |
// Shift the current position of the content_parent so | |
// that the view doesn't 'jump' | |
m_content_position->Assign( | |
std::max<float>( | |
m_content_position->Get()+position_shift, | |
0.0f)); | |
} | |
void clampContentPositionToSize() | |
{ | |
if(m_content_size->Get() > m_view_size->Get()) | |
{ | |
if(m_content_position->Get() > 0) | |
{ | |
m_content_position->Assign(0); | |
} | |
else if(m_content_position->Get() < | |
(m_view_size->Get()-m_content_size->Get())) | |
{ | |
m_content_position->Assign( | |
m_view_size->Get()-m_content_size->Get()); | |
} | |
} | |
} | |
void updateContentParentSize() | |
{ | |
float estimated_size = | |
(m_average_delegate_size+spacing.Get())* | |
m_list_model->GetSize(); | |
estimated_size -= spacing.Get(); | |
if(m_list_delegates.empty()) | |
{ | |
m_content_size->Assign(estimated_size); | |
return; | |
} | |
auto& last_delegate = m_list_delegates.back(); | |
float last_delegate_end = | |
m_get_delegate_position(last_delegate)+ | |
m_get_delegate_size(last_delegate); | |
if(last_delegate->GetIndex() == | |
m_list_model->GetSize()-1) | |
{ | |
m_content_size->Assign(last_delegate_end); | |
} | |
else | |
{ | |
m_content_size->Assign( | |
std::max( | |
last_delegate_end, | |
estimated_size)); | |
} | |
} | |
void calcDelegateBounds() | |
{ | |
if(layout.Get() == ListViewProperties::Layout::Column) | |
{ | |
m_delegate_bounds_top = | |
[this](){ | |
return -1.0f*m_content_parent->y.Get()- | |
delegate_extents.Get(); | |
}; | |
m_delegate_bounds_bottom = | |
[this](){ | |
return m_delegate_bounds_top.Get()+ | |
height.Get()+ | |
2*delegate_extents.Get(); | |
}; | |
m_delegate_bounds_left = | |
[this](){ | |
return m_content_parent->x.Get(); | |
}; | |
m_delegate_bounds_right = | |
[this](){ | |
return m_delegate_bounds_left.Get()+ | |
width.Get(); | |
}; | |
} | |
else // Row | |
{ | |
throw ListViewTODO(); | |
} | |
} | |
void calcAverageDelegateSize() | |
{ | |
if(m_list_model->GetSize() == 0) | |
{ | |
return; | |
} | |
m_average_delegate_size=0; | |
if(m_list_delegates.size() > 0) | |
{ | |
for(auto& delegate : m_list_delegates) | |
{ | |
m_average_delegate_size += m_get_delegate_size(delegate); | |
} | |
m_average_delegate_size /= (float)m_list_delegates.size(); | |
} | |
else | |
{ | |
// Instantiate some delegates | |
// TODO is 5 enough? | |
uint const delegate_count = | |
std::min<uint>(5,m_list_model->GetSize()); | |
for(uint i=0; i < delegate_count; i++) | |
{ | |
auto delegate = | |
ks::make_object<DelegateType>( | |
m_content_parent); | |
delegate->SetData(m_list_model->GetData(i)); | |
delegate->SetIndex(i); | |
m_average_delegate_size += m_get_delegate_size(delegate); | |
m_content_parent->RemoveChild(delegate); | |
} | |
m_average_delegate_size /= (float)delegate_count; | |
} | |
// Enforce a minimum height | |
if(m_average_delegate_size < 1*mm) | |
{ | |
m_average_delegate_size = 1*mm; | |
} | |
} | |
void eraseDelegatesOutsideExtents(float vext_t, | |
float vext_b, | |
float vext_l, | |
float vext_r) | |
{ | |
uint remove_count=0; | |
for(auto it = m_list_delegates.end(); | |
it != m_list_delegates.begin();) | |
{ | |
--it; | |
auto& delegate = *it; | |
float delegate_t = delegate->y.Get(); | |
float delegate_b = delegate_t + delegate->height.Get(); | |
float delegate_l = delegate->x.Get(); | |
float delegate_r = delegate_l + delegate->width.Get(); | |
bool outside_vext = | |
(delegate_b < vext_t) || | |
(delegate_t > vext_b) || | |
(delegate_r < vext_l) || | |
(delegate_l > vext_r); | |
if(outside_vext) | |
{ | |
remove_count++; | |
m_content_parent->RemoveChild(delegate); | |
it = m_list_delegates.erase(it); | |
} | |
} | |
} | |
void createDebugGuidelines() | |
{ | |
if(layout.Get() == ListViewProperties::Layout::Column) | |
{ | |
auto& lgd = m_list_guidelines; | |
lgd.clear(); | |
// Create guidelines for the delegate extents window | |
// top | |
lgd.push_back( | |
ks::make_object<Rectangle>( | |
m_content_parent, | |
name+"guideline_delegates_top")); | |
lgd.back()->width = [this](){ return width.Get(); }; | |
lgd.back()->height = 0.25f*mm; | |
lgd.back()->y = [this](){ return m_delegate_bounds_top.Get(); }; | |
// bottom | |
lgd.push_back( | |
ks::make_object<Rectangle>( | |
m_content_parent, | |
name+"guideline_delegates_bottom")); | |
lgd.back()->width = [this](){ return width.Get(); }; | |
lgd.back()->height = 0.25f*mm; | |
lgd.back()->y = [this](){ return m_delegate_bounds_bottom.Get(); }; | |
// Create guidelines for the list view content edges | |
// top | |
lgd.push_back( | |
ks::make_object<Rectangle>( | |
m_content_parent, | |
name+"guideline_content_top")); | |
lgd.back()->width = [this](){ return width.Get(); }; | |
lgd.back()->height = 0.25f*mm; | |
lgd.back()->y = [this](){ return m_content_parent->y.Get(); }; | |
lgd.back()->color = glm::u8vec3{255,0,255}; | |
// bottom | |
lgd.push_back( | |
ks::make_object<Rectangle>( | |
m_content_parent, | |
name+"guideline_content_bottom")); | |
lgd.back()->width = [this](){ return width.Get(); }; | |
lgd.back()->height = 0.25f*mm; | |
lgd.back()->y = [this](){ return m_content_parent->height.Get()-0.25f*mm; }; | |
lgd.back()->color = glm::u8vec3{255,0,255}; | |
log.Trace() << "height: " << m_content_parent->height.Get(); | |
} | |
else | |
{ | |
throw ListViewTODO(); | |
} | |
} | |
}; | |
// =========================================================== // | |
// =========================================================== // | |
// =========================================================== // | |
} | |
// =========================================================== // | |
// =========================================================== // | |
using namespace raintk; | |
int main(int argc, char* argv[]) | |
{ | |
(void)argc; | |
(void)argv; | |
TestContext c(480,800); | |
auto root = c.scene->GetRootWidget(); | |
c.scene->SetShowDebugText(false); | |
auto list_view_bgbg = | |
ks::make_object<Rectangle>( | |
root,"list_view_bgbg"); | |
list_view_bgbg->width = 60*mm; | |
list_view_bgbg->height = 150*mm; | |
list_view_bgbg->x = 0.5*(root->width.Get()-list_view_bgbg->width.Get()); | |
list_view_bgbg->y = 0.5*(root->height.Get()-list_view_bgbg->height.Get()); | |
list_view_bgbg->z = 0.5*mm; | |
list_view_bgbg->color = glm::u8vec3(30,60,60); | |
auto list_view_bg = | |
ks::make_object<Rectangle>( | |
root,"list_view_bg"); | |
list_view_bg->width = 60*mm; | |
list_view_bg->height = 100*mm; | |
list_view_bg->x = 0.5*(root->width.Get()-list_view_bg->width.Get()); | |
list_view_bg->y = 0.5*(root->height.Get()-list_view_bg->height.Get()); | |
list_view_bg->z = 1.0; | |
list_view_bg->color = glm::u8vec3(60,60,60); | |
// ListModel | |
auto list_model = make_shared<ListModelSTLVector<TestItem>>(); | |
for(uint i=0; i < 30; i++) | |
{ | |
list_model->PushBack(TestItem{glm::u8vec3{96,200,90}}); | |
} | |
// ListView | |
auto list_view = | |
ks::make_object<ListView<TestItem,TestDelegate>>( | |
list_view_bg,"list_view"); | |
list_view->width = list_view_bg->width.Get(); | |
list_view->height = list_view_bg->height.Get(); | |
list_view->z = 1.5*mm; | |
list_view->spacing = 2.5*mm; | |
list_view->SetListModel(list_model); | |
// Run! | |
c.app->Run(); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment