75 using typename
base_type::optional_sort_direction;
77 using typename
base_type::optional_sort_by_param;
78 using typename
base_type::filter_search_key;
79 using typename
base_type::filter_search_type;
80 using typename
base_type::case_sensitivity;
82 typedef ItemModel item_model_type;
83 typedef typename item_model_type::container_traits::template rebind<
item_model_index::row_type, cell_meta_type, true>::other container_traits;
84 typedef typename container_traits::row_cell_array row_cell_array;
85 typedef typename container_traits::container_type container_type;
86 typedef typename container_traits::const_skip_iterator const_iterator;
87 typedef typename container_traits::skip_iterator iterator;
88 typedef typename container_traits::const_sibling_iterator const_sibling_iterator;
89 typedef typename container_traits::sibling_iterator sibling_iterator;
90 typedef typename container_traits::allocator_type allocator_type;
91 typedef typename container_type::value_type row_type;
94 typedef
std::
vector<item_presentation_model_index::optional_row_type, typename
std::allocator_traits<allocator_type>:: template rebind_alloc<item_presentation_model_index::optional_row_type>> row_map_type;
95 typedef
std::
vector<item_presentation_model_index::optional_column_type, typename
std::allocator_traits<allocator_type>:: template rebind_alloc<item_presentation_model_index::optional_column_type>> column_map_type;
102 mutable std::map<dimension, uint32_t> cellWidths;
103 mutable std::optional<std::string> headingText;
104 mutable font headingFont;
108 void add_cell_width(
dimension aWidth)
const
110 ++cellWidths[aWidth];
112 void remove_cell_width(
dimension aWidth)
const
114 auto existing = cellWidths.find(aWidth);
115 if (existing != cellWidths.end())
117 if (--existing->second == 0)
118 cellWidths.erase(existing);
122 typedef typename container_traits::template rebind<item_presentation_model_index::row_type, column_info>::other::row_cell_array column_info_array;
124 using typename base_type::no_item_model;
125 using typename base_type::bad_index;
126 using typename base_type::no_mapped_row;
132 basic_item_presentation_model(i_item_model& aItemModel,
bool aSortable =
false) : iItemModel{ nullptr }, iSortable{ aSortable }, iAlternatingRowColor{ false }
141 iItemModelSink.
clear();
150 return iAttachment !=
nullptr;
154 if (iAttachment ==
nullptr)
155 throw not_attached();
160 if (iAttachment && aWidget !=
nullptr)
161 throw already_attached();
162 iAttachment = aWidget;
166 if (iAttachment ==
nullptr)
167 throw not_attached();
168 iAttachment =
nullptr;
173 return iUpdating != 0u;
177 if (++iUpdating == 1)
178 ItemsUpdating.trigger();
182 if (--iUpdating == 0)
188 ItemsUpdated.trigger();
193 return iItemModel !=
nullptr;
197 if (iItemModel ==
nullptr)
198 throw no_item_model();
199 return static_cast<item_model_type&
>(*iItemModel);
203 if (iItemModel != &aItemModel)
205 auto reset_model = [
this]()
208 scoped_item_update siu{ *
this };
211 iColumns.emplace_back(col);
219 iItemModelSink.
clear();
220 iItemModel = &aItemModel;
226 iItemModelSink +=
item_model().cleared([
this]()
234 iItemModelSink +=
item_model().destroying([
this]()
236 iItemModel =
nullptr;
252 return aIndex.row() < row_map().size() && row_map()[aIndex.row()];
257 return item_presentation_model_index{ mapped_row(aIndex.
row()), !aIgnoreColumn ? mapped_column(aIndex.
column()) : 0 };
263 if constexpr (container_traits::is_flat)
264 return static_cast<uint32_t
>(iRows.size());
266 return static_cast<uint32_t
>(iRows.ksize());
270 return static_cast<uint32_t
>(iColumns.size());
272 uint32_t
columns(item_presentation_model_index
const& aIndex)
const final
274 return static_cast<uint32_t
>(
row(aIndex).cells.size());
277 void accept(i_meta_visitor& aVisitor,
bool aIgnoreCollapsedState =
false) final
279 if constexpr (container_traits::is_flat)
281 for (
auto row = iRows.begin();
row != iRows.end(); ++
row)
283 aVisitor.visit(
cell);
285 else if (!aIgnoreCollapsedState)
287 for (
auto row = iRows.kbegin();
row != iRows.kend(); ++
row)
289 aVisitor.visit(
cell);
293 for (
auto row = iRows.begin();
row != iRows.end(); ++
row)
295 aVisitor.visit(
cell);
303 auto& cellWidths = column(aColumnIndex).cellWidths;
304 if (!cellWidths.empty())
306 for (item_presentation_model_index::row_type
row = 0u;
row <
rows(); ++
row)
307 cell_extents(item_presentation_model_index{
row, aColumnIndex }, aUnitsContext);
310 std::string
const&
column_heading_text(item_presentation_model_index::column_type aColumnIndex)
const override
312 if (column(aColumnIndex).headingText != std::nullopt)
313 return *column(aColumnIndex).headingText;
314 else if (has_model_column(aColumnIndex))
315 return item_model().column_name(model_column(aColumnIndex));
318 static std::string
const none;
324 if (column(aColumnIndex).headingFont != font{})
326 column(aColumnIndex).headingFont = font{};
327 column(aColumnIndex).headingExtents = std::nullopt;
329 if (column(aColumnIndex).headingExtents != std::nullopt)
334 multiline_text_extent(
column_heading_text(aColumnIndex), column(aColumnIndex).headingFont);
340 column(aColumnIndex).headingText = aHeadingText;
341 column(aColumnIndex).headingExtents = std::nullopt;
342 ColumnInfoChanged.trigger(aColumnIndex);
347 return column(aColumnIndex).flags;
352 if (column(aColumnIndex).flags != aFlags)
354 column(aColumnIndex).flags = aFlags;
355 ColumnInfoChanged.trigger(aColumnIndex);
360 return column(aColumnIndex).imageSize;
364 if (column(aColumnIndex).imageSize != aImageSize)
366 column(aColumnIndex).imageSize = aImageSize;
368 ColumnInfoChanged.trigger(aColumnIndex);
371 bool expand(item_presentation_model_index
const& aIndex)
final
373 if constexpr (container_traits::is_tree)
375 if (!
cell_meta(aIndex.with_column(0)).expanded)
383 bool collapse(item_presentation_model_index
const& aIndex)
final
385 if constexpr (container_traits::is_tree)
387 if (
cell_meta(aIndex.with_column(0)).expanded)
397 if constexpr (container_traits::is_tree)
401 item_presentation_model_index
const indexFirstColumn{ aIndex.row() };
402 if (!
cell_meta(indexFirstColumn).expanded)
403 ItemExpanding.trigger(aIndex);
405 ItemCollapsing.trigger(aIndex);
407 if (
cell_meta(indexFirstColumn).expanded)
408 std::next(begin(), aIndex.row()).unskip_children();
410 std::next(begin(), aIndex.row()).skip_children();
413 if (
cell_meta(indexFirstColumn).expanded)
414 ItemExpanded.trigger(aIndex);
416 ItemCollapsed.trigger(aIndex);
424 if constexpr (container_traits::is_tree)
434 const button_checked_state&
checked_state(item_presentation_model_index
const& aIndex)
final
438 bool is_checked(item_presentation_model_index
const& aIndex)
const final
440 return cell_meta(aIndex).checked ==
true;
442 bool is_unchecked(item_presentation_model_index
const& aIndex)
const final
444 return cell_meta(aIndex).checked ==
false;
448 return cell_meta(aIndex).checked == std::nullopt;
450 void set_checked_state(item_presentation_model_index
const& aIndex, button_checked_state
const& aState)
final
455 if (aState == std::nullopt && !cell_tri_state_checkable(aIndex))
456 throw not_tri_state_checkable();
458 ItemToggled.trigger(aIndex);
460 ItemChecked.trigger(aIndex);
462 ItemUnchecked.trigger(aIndex);
464 ItemIndeterminate.trigger(aIndex);
467 void check(item_presentation_model_index
const& aIndex)
final
471 void uncheck(item_presentation_model_index
const& aIndex)
final
479 void set_checked(item_presentation_model_index
const& aIndex,
bool aChecked)
final
487 if (cell_tri_state_checkable(aIndex))
499 if (iDefaultFont != std::nullopt)
500 return *iDefaultFont;
501 return service<i_app>().current_style().font();
505 if (iDefaultFont != aDefaultFont)
507 iDefaultFont = aDefaultFont;
513 if (iCellSpacing == std::nullopt)
515 std::optional<scoped_units> su;
517 su.emplace(
attachment(), scoped_units::current_units());
518 size result{ 1.0_mm, 1.0_mm };
519 if (to_px<uint32_t>(result.cx) % 2u == 1u)
520 result.
cx = from_px<dimension>(to_px<uint32_t>(result.cx) + 1u);
521 if (to_px<uint32_t>(result.cy) % 2u == 1u)
522 result.cy = from_px<dimension>(to_px<uint32_t>(result.cy) + 1u);
529 if (aSpacing == std::nullopt)
530 iCellSpacing = aSpacing;
536 if (iCellPadding == std::nullopt)
544 if (aPadding == std::nullopt)
545 iCellPadding = aPadding;
551 return iAlternatingRowColor;
555 iAlternatingRowColor = aAlternatingColor;
561 for (uint32_t col = 0; col <
row(aIndex).cells.size(); ++col)
563 auto const index = item_presentation_model_index{ aIndex.row(), col };
565 if (
cell_meta(index).extents != std::nullopt)
571 auto const& effectiveFont = (cellFont == std::nullopt ?
default_font() : *cellFont);
572 height = std::max(height,
units_converter(aUnitsContext).from_device_units(
size{ 0.0, std::ceil(effectiveFont.height()) }).cy *
573 (1 + std::count(cellString.begin(), cellString.end(),
'\n')));
575 if (maybeCellImageSize != std::nullopt)
576 height = std::max(height,
units_converter(aUnitsContext).from_device_units(*maybeCellImageSize).cy);
577 auto const& cellInfo =
item_model().cell_info(modelIndex);
578 if (cell_editable(index) && cellInfo.dataStep !=
neolib::none)
586 if (iTotalHeight != std::nullopt)
587 return *iTotalHeight;
589 for (item_presentation_model_index::row_type
row = 0;
row <
rows(); ++
row)
590 height +=
item_height(item_presentation_model_index(
row, 0), aUnitsContext);
591 iTotalHeight = height;
592 return *iTotalHeight;
596 if (iPositions[aIndex.row()] == std::nullopt)
598 auto pred = [](
const optional_position& lhs,
const optional_position& rhs) ->
bool
600 if (lhs == std::nullopt && rhs == std::nullopt)
602 else if (lhs != std::nullopt && rhs == std::nullopt)
604 else if (lhs == std::nullopt && rhs != std::nullopt)
609 auto i = std::lower_bound(iPositions.
begin(), iPositions.
end(), optional_position(), pred);
610 auto row =
static_cast<item_presentation_model_index::row_type
>(
std::distance(iPositions.
begin(), i));
612 while (
row <= aIndex.row())
614 iPositions[
row] = position;
615 position +=
item_height(item_presentation_model_index(
row), aUnitsContext);
619 return *iPositions[aIndex.row()];
621 std::pair<item_presentation_model_index::row_type, coordinate>
item_at(
double aPosition,
i_units_context const& aUnitsContext)
const final
624 return std::pair<item_presentation_model_index::row_type, coordinate>{ 0u, 0.0 };
625 auto pred = [](
const optional_position& lhs,
const optional_position& rhs) ->
bool
627 if (lhs == std::nullopt && rhs == std::nullopt)
629 else if (lhs != std::nullopt && rhs == std::nullopt)
631 else if (lhs == std::nullopt && rhs != std::nullopt)
636 auto i = std::lower_bound(iPositions.
begin(), iPositions.
end(), aPosition, pred);
637 if ((i == iPositions.
end() || (*i != std::nullopt && **i > aPosition)) && i != iPositions.
begin())
639 while (*i == std::nullopt)
641 auto j = std::lower_bound(iPositions.
begin(), iPositions.
end(), optional_position(), pred);
642 auto row =
static_cast<item_presentation_model_index::row_type
>(
std::distance(iPositions.
begin(), j));
644 while (
row < iPositions.
size())
646 iPositions[
row] = position;
647 position +=
item_height(item_presentation_model_index(
row), aUnitsContext);
650 i = std::lower_bound(iPositions.
begin(), iPositions.
end(), aPosition, pred);
651 if ((i == iPositions.
end() || (*i != std::nullopt && **i > aPosition)) && i != iPositions.
begin())
654 auto const result = std::pair<item_presentation_model_index::row_type, coordinate>{
static_cast<item_presentation_model_index::row_type
>(
std::distance(iPositions.
begin(), i)),
static_cast<coordinate>(**i - aPosition) };
660 if (
cell_meta(aIndex).flags != std::nullopt)
669 ItemChanged.trigger(aIndex);
672 cell_meta_type&
cell_meta(item_presentation_model_index
const& aIndex)
const final
674 if (aIndex.row() <
rows())
676 if (aIndex.column() >=
row(aIndex).cells.size())
678 row(aIndex).cells.resize(aIndex.column() + 1);
679 if constexpr (container_traits::is_tree)
680 if (aIndex.column() == 0)
681 row(aIndex).cells[aIndex.column()].expanded = !
std::next(begin(), aIndex.row()).children_skipped();
683 return row(aIndex).cells[aIndex.column()];
688 std::string
cell_to_string(item_presentation_model_index
const& aIndex)
const final
691 return std::visit([&,
this](
auto&& arg) -> std::string
693 typedef std::decay_t<
decltype(arg)> type;
700 item_cell_data
string_to_cell_data(item_presentation_model_index
const& aIndex, std::string
const& aString)
const final
705 item_cell_data
string_to_cell_data(item_presentation_model_index
const& aIndex, std::string
const& aString,
bool& aError)
const final
709 std::istringstream input{ aString };
711 switch (cellInfo.dataType)
716 if (!(input >> value) || (input >> guff))
726 if (!(input >> value) || (input >> guff))
732 value = std::max(value, static_variant_cast<int32_t>(cellInfo.dataMin));
734 value = std::min(value, static_variant_cast<int32_t>(cellInfo.dataMax));
740 if (!(input >> value) || (input >> guff))
746 value = std::max(value, static_variant_cast<uint32_t>(cellInfo.dataMin));
748 value = std::min(value, static_variant_cast<uint32_t>(cellInfo.dataMax));
754 if (!(input >> value) || (input >> guff))
760 value = std::max(value, static_variant_cast<int64_t>(cellInfo.dataMin));
762 value = std::min(value, static_variant_cast<int64_t>(cellInfo.dataMax));
768 if (!(input >> value) || (input >> guff))
774 value = std::max(value, static_variant_cast<uint64_t>(cellInfo.dataMin));
776 value = std::min(value, static_variant_cast<uint64_t>(cellInfo.dataMax));
782 if (!(input >> value) || (input >> guff))
788 value = std::max(value, static_variant_cast<float>(cellInfo.dataMin));
790 value = std::min(value, static_variant_cast<float>(cellInfo.dataMax));
796 if (!(input >> value) || (input >> guff))
802 value = std::max(value, static_variant_cast<double>(cellInfo.dataMin));
804 value = std::min(value, static_variant_cast<double>(cellInfo.dataMax));
809 if (!aString.empty())
810 return string{ aString };
815 boost::basic_format<char>
cell_format(item_presentation_model_index
const&)
const override
817 static const boost::basic_format<char> sDefaultFormat(
"%1%");
818 return sDefaultFormat;
824 optional_font
cell_font(item_presentation_model_index
const&)
const override
826 return optional_font{};
830 if (column(aIndex.column()).imageSize)
831 return column(aIndex.column()).imageSize;
838 if (cell_checkable(aIndex))
840 auto const& cellFont =
cell_font(aIndex);
841 auto const& effectiveFont = (cellFont == std::nullopt ?
default_font() : *cellFont);
850 if constexpr (container_traits::is_flat)
853 return size{ 16.0_dip };
861 if (
cell_meta(aIndex).text != std::nullopt)
863 auto const& cellFont =
cell_font(aIndex);
864 auto const& effectiveFont = (cellFont == std::nullopt ?
default_font() : *cellFont);
871 auto oldItemHeight =
item_height(aIndex, aUnitsContext);
872 auto const& cellFont =
cell_font(aIndex);
873 auto const& effectiveFont = (cellFont == std::nullopt ?
default_font() : *cellFont);
875 if (cellMeta.extents != std::nullopt)
879 if (cell_editable(aIndex) && cellInfo.dataStep !=
neolib::none)
881 cellExtents.
cx = std::max(cellExtents.
cx,
graphics_context{ attachment(), graphics_context::type::Unattached }.
882 text_extent(cellInfo.dataMax.to_string(), effectiveFont).cx);
886 cellExtents.
cx +=
indent(aIndex, aUnitsContext);
888 if (maybeCheckBoxSize != std::nullopt)
891 cellExtents.
cy = std::max(cellExtents.
cy, maybeCheckBoxSize->cy);
894 if (maybeCellImageSize != std::nullopt)
897 cellExtents.
cy = std::max(cellExtents.
cy, maybeCellImageSize->cy);
899 cellExtents.
cy = std::max(cellExtents.
cy, effectiveFont.height());
900 cache_cell_meta_extents(aIndex, cellExtents.
ceil());
901 if (iTotalHeight != std::nullopt)
902 *iTotalHeight += (
item_height(aIndex, aUnitsContext) - oldItemHeight);
907 if constexpr (container_traits::is_flat)
909 else if (aIndex.column() != 0)
914 auto treeIter = baseIter.get<
typename item_model_type::const_iterator,
typename item_model_type::const_iterator,
915 typename item_model_type::iterator,
typename item_model_type::const_sibling_iterator,
typename item_model_type::sibling_iterator>();
920 void sort(i_item_sort_predicate
const& aPredicate)
final
923 ItemsSorting.trigger();
924 if constexpr (container_traits::is_flat)
925 std::sort(iRows.begin(), iRows.end(), [&](
auto const& lhs,
auto const& rhs) {
return aPredicate.compare(lhs.value, rhs.value); });
927 iRows.sort([&](
auto const& lhs,
auto const& rhs) {
return aPredicate.compare(lhs.value, rhs.value); });
929 reset_position_meta(0);
930 ItemsSorted.trigger();
938 iSortable = aSortable;
942 if (!iSortOrder.empty())
943 return iSortOrder.front();
945 return optional_sort_by_param{};
947 void sort_by(item_presentation_model_index::column_type aColumnIndex,
const optional_sort_direction& aSortDirection = optional_sort_direction{})
final
949 iSortOrder.push_front(sort_by_param{ aColumnIndex, aSortDirection == std::nullopt ? sort_direction::Ascending : *aSortDirection });
950 for (
auto i =
std::next(iSortOrder.begin()); i != iSortOrder.end(); ++i)
952 if (i->first == aColumnIndex)
954 if (aSortDirection == std::nullopt)
957 iSortOrder.front().second = (i->second == sort_direction::Ascending ? sort_direction::Descending : sort_direction::Ascending);
959 iSortOrder.front().second = i->second;
974 optional_item_presentation_model_index
find_item(filter_search_key
const& aFilterSearchKey, item_presentation_model_index::column_type aColumnIndex = 0,
975 filter_search_type aFilterSearchType = filter_search_type::Prefix, case_sensitivity aCaseSensitivity = case_sensitivity::CaseInsensitive)
const final
977 for (item_presentation_model_index::row_type
row = 0;
row <
rows(); ++
row)
980 auto const& origValue =
item_model().cell_data(modelIndex).to_string();
981 auto const& value = aCaseSensitivity == case_sensitivity::CaseSensitive ? origValue : boost::to_upper_copy<std::string>(origValue);
982 auto const& origKey = aFilterSearchKey;
983 auto const& key = aCaseSensitivity == case_sensitivity::CaseSensitive ? origKey : boost::to_upper_copy<std::string>(origKey);
986 switch (aFilterSearchType)
988 case filter_search_type::Prefix:
989 if (value.size() >= key.size() && value.substr(0, key.size()) == key)
993 return optional_item_presentation_model_index{};
1002 if (!iFilters.empty())
1003 return iFilters.front();
1005 return optional_filter{};
1007 void filter_by(item_presentation_model_index::column_type aColumnIndex, filter_search_key
const& aFilterSearchKey,
1008 filter_search_type aFilterSearchType = filter_search_type::Value, case_sensitivity aCaseSensitivity = case_sensitivity::CaseInsensitive)
final
1010 iFilters.push_back(filter{ aColumnIndex, aFilterSearchKey, aFilterSearchType, aCaseSensitivity });
1011 for (
auto i = iFilters.begin(); i !=
std::prev(iFilters.end()); ++i)
1013 if (std::get<0>(*i) == aColumnIndex)
1023 if (!iFilters.empty())
1032 iSink = service<i_rendering_engine>().subpixel_rendering_changed([
this]()
1036 iSink += service<i_app>().current_style_changed([
this](
style_aspect aAspect)
1044 void execute_sort(
bool aForce =
false)
1050 if (iSortOrder.empty())
1052 sort_by(0, sort_direction::Ascending);
1055 ItemsSorting.trigger();
1056 auto sortPredicate = [&](
const typename container_type::value_type& aLhs,
const typename container_type::value_type& aRhs) ->
bool
1058 for (std::size_t i = 0; i < iSortOrder.size(); ++i)
1060 auto col = iSortOrder[i].first;
1061 auto const& v1 =
item_model().cell_data(item_model_index{ aLhs.value, model_column(col) });
1062 auto const& v2 =
item_model().cell_data(item_model_index{ aRhs.value, model_column(col) });
1063 if (std::holds_alternative<string>(v1) && std::holds_alternative<string>(v2))
1065 std::string s1 = boost::to_upper_copy<std::string>(std::get<string>(v1));
1066 std::string s2 = boost::to_upper_copy<std::string>(std::get<string>(v2));
1068 return iSortOrder[i].second == sort_direction::Ascending;
1070 return iSortOrder[i].second == sort_direction::Descending;
1073 return iSortOrder[i].second == sort_direction::Ascending;
1075 return iSortOrder[i].second == sort_direction::Descending;
1079 if constexpr (container_traits::is_flat)
1080 std::sort(iRows.begin(), iRows.end(), sortPredicate);
1082 iRows.sort(sortPredicate);
1084 reset_position_meta(0);
1085 ItemsSorted.trigger();
1087 void execute_filter()
1090 scoped_item_update siu{ *
this };
1092 ItemsFiltering.trigger();
1096 bool matches =
true;
1097 for (
auto const& filter : iFilters)
1099 auto const& origValue =
item_model().cell_data(item_model_index{ row, model_column(std::get<0>(filter)) }).
to_string();
1100 auto const& value = (std::get<3>(filter) == case_sensitivity::CaseSensitive ? origValue : boost::to_upper_copy<std::string>(origValue));
1101 auto const& origKey = std::get<1>(filter);
1102 auto const& key = (std::get<3>(filter) == case_sensitivity::CaseSensitive ? origKey : boost::to_upper_copy<std::string>(origKey));
1105 switch (std::get<2>(filter))
1107 case filter_search_type::Prefix:
1108 if (value.size() < key.size() || value.substr(0, key.size()) != key)
1111 case filter_search_type::Glob:
1114 case filter_search_type::Regex:
1123 ItemsFiltered.trigger();
1129 reset_column_map(
false);
1133 reset_cell_meta(index.column());
1134 reset_column_meta(index.column());
1135 ColumnInfoChanged.trigger(index.column());
1138 void item_added(
const item_model_index& aItemIndex)
1140 if constexpr (container_traits::is_tree)
1143 for (
auto& row : iRows)
1144 if (row.value >= aItemIndex.row())
1146 if constexpr (container_traits::is_flat)
1147 iRows.push_back(row_type{ aItemIndex.row() });
1152 auto const pos = iRows.csend();
1153 iRows.insert(pos, row_type{ aItemIndex.row() });
1158 auto const pos = const_sibling_iterator{
std::next(iRows.cbegin(), parentIndex.row()) };
1159 iRows.insert(pos.end(), row_type{ aItemIndex.row() });
1163 if (!
updating() || container_traits::is_tree)
1164 reset_row_map(aItemIndex);
1168 reset_position_meta(aItemIndex.row());
1173 void item_changed(
const item_model_index& aItemIndex)
1175 if (!has_item_model_index(aItemIndex))
1180 reset_position_meta(aItemIndex.row());
1182 auto const index = from_item_model_index(aItemIndex);
1183 auto& cellMeta = cell_meta(index);
1184 cellMeta.text = std::nullopt;
1185 cache_cell_meta_extents(index, std::nullopt);
1187 cell_extents(index, attachment());
1188 ItemChanged.trigger(from_item_model_index(aItemIndex));
1191 void item_removing(
const item_model_index& aItemIndex)
1193 if (!has_item_model_index(aItemIndex))
1195 auto const index = from_item_model_index(aItemIndex);
1196 for (item_presentation_model_index::column_type col = 0; col < columns(); ++col)
1197 cache_cell_meta_extents(index.with_column(col), std::nullopt);
1199 ItemRemoving.trigger(index);
1200 iRows.erase(
std::next(begin(), index.row()));
1201 for (
auto& row : iRows)
1202 if (row.value >= aItemIndex.row())
1204 if (iRowMap[aItemIndex.row()])
1205 for (
auto& row : iRowMap)
1206 if (row && *row >= index.row())
1208 iRowMap.erase(
std::next(iRowMap.begin(), aItemIndex.row()));
1211 reset_position_meta(index.row());
1212 ItemRemoved.trigger(index);
1215 void item_removed(
const item_model_index& aItemIndex)
1219 void cache_cell_meta_extents(item_presentation_model_index
const& aIndex,
const optional_size& aExtents = {})
const
1221 auto& cellMeta = cell_meta(aIndex);
1222 if (aExtents != cellMeta.extents)
1224 auto previousExtents = cellMeta.extents;
1225 cellMeta.extents = aExtents;
1226 if (previousExtents)
1227 column(aIndex.column()).remove_cell_width(previousExtents->cx);
1228 if (cellMeta.extents)
1229 column(aIndex.column()).add_cell_width(cellMeta.extents->cx);
1232 void reset_maps(
const item_model_index& aFrom = {})
const
1234 reset_row_map(aFrom);
1237 void reset_row_map(
const item_model_index& aFrom = {})
const
1239 if (aFrom.row() < iRowMap.size() && (iRowMapDirtyFrom == std::nullopt || *iRowMapDirtyFrom > aFrom.row()))
1240 iRowMapDirtyFrom = aFrom.row();
1242 void reset_column_map(
bool aClear =
true)
const
1246 if (has_item_model())
1252 if (aRowIndex < row_map().
size() && row_map()[aRowIndex])
1253 return *row_map()[aRowIndex];
1254 throw no_mapped_row();
1256 const row_map_type& row_map()
const
1258 if (iRowMapDirtyFrom)
1259 iRowMap.erase(
std::next(iRowMap.begin(), *iRowMapDirtyFrom), iRowMap.end());
1260 iRowMapDirtyFrom = std::nullopt;
1264 for (item_presentation_model_index::row_type row = 0; row < rows(); ++row)
1265 iRowMap[self_type::row(row).value] = row;
1269 row_map_type& row_map()
1271 return const_cast<row_map_type&
>(
to_const(*this).row_map());
1275 if (aColumnIndex >= iColumnMap.size())
1276 iColumnMap.resize(aColumnIndex + 1);
1277 auto& mapCol = iColumnMap[aColumnIndex];
1280 for (item_presentation_model_index::column_type col = 0; col < columns(); ++col)
1281 if (iColumns[col].modelColumn == aColumnIndex)
1285 auto newColumn = columns();
1286 for (item_presentation_model_index::column_type col = 0; col < columns(); ++col)
1287 if (iColumns[col].modelColumn == std::nullopt)
1292 column(newColumn).modelColumn = aColumnIndex;
1298 bool has_model_column(item_presentation_model_index::column_type aColumnIndex)
const
1300 auto const& col = column(aColumnIndex);
1301 if (col.modelColumn)
1302 reset_column_map(
false);
1303 return col.modelColumn.has_value();
1307 auto const& col = column(aColumnIndex);
1308 if (col.modelColumn)
1309 return *col.modelColumn;
1310 reset_column_map(
false);
1311 if (col.modelColumn)
1312 return *col.modelColumn;
1315 const column_map_type& column_map()
const
1319 column_map_type& column_map()
1323 void reset_meta()
const
1326 reset_column_meta();
1327 reset_position_meta(0);
1330 for (item_presentation_model_index::row_type row = 0; row < rows(); ++row)
1331 for (item_presentation_model_index::column_type col = 0; col < iColumns.size(); ++col)
1332 cell_extents(item_presentation_model_index{row, col}, attachment());
1334 void reset_cell_meta(
const std::optional<item_presentation_model_index::column_type>& aColumn = {})
const
1336 for (item_presentation_model_index::row_type row = 0; row < rows(); ++row)
1338 for (item_presentation_model_index::column_type col = 0; col < iColumns.size(); ++col)
1340 if (aColumn != std::nullopt && col != *aColumn)
1342 item_presentation_model_index
const index{ row, col };
1343 cell_meta(index).text = std::nullopt;
1344 cache_cell_meta_extents(index, std::nullopt);
1348 void reset_column_meta(
const std::optional<item_presentation_model_index::column_type>& aColumn = {})
const
1350 for (item_presentation_model_index::column_type col = 0; col < iColumns.size(); ++col)
1352 if (aColumn != std::nullopt && col != *aColumn)
1354 column(col).headingExtents = std::nullopt;
1357 void reset_position_meta(item_presentation_model_index::row_type aFromRow)
const
1359 iTotalHeight = std::nullopt;
1360 iPositions.resize(rows());
1361 for (std::size_t i = aFromRow; i < iPositions.size(); ++i)
1362 iPositions[i] = std::nullopt;
1365 const_iterator cbegin()
const
1367 if constexpr (container_traits::is_flat)
1368 return iRows.begin();
1370 return iRows.kbegin();
1372 const_iterator begin()
const
1378 if constexpr (container_traits::is_flat)
1379 return iRows.begin();
1381 return iRows.kbegin();
1383 const_iterator cend()
const
1385 if constexpr (container_traits::is_flat)
1388 return iRows.kend();
1390 const_iterator end()
const
1396 if constexpr (container_traits::is_flat)
1399 return iRows.kend();
1402 const row_type& row(item_presentation_model_index::row_type aRow)
const
1406 const row_type& row(item_presentation_model_index aIndex)
const
1408 return row(aIndex.row());
1410 row_type& row(item_presentation_model_index::row_type aRow)
1414 row_type& row(item_presentation_model_index aIndex)
1416 return row(aIndex.row());
1418 const column_info& column(item_presentation_model_index::column_type aColumnIndex)
const
1420 while(iColumns.size() <= aColumnIndex)
1421 iColumns.emplace_back();
1422 return iColumns[aColumnIndex];
1424 column_info& column(item_presentation_model_index::column_type aColumnIndex)
1426 return const_cast<column_info&
>(
to_const(*this).column(aColumnIndex));
1429 weak_ref_ptr<i_widget> iAttachment;
1430 i_item_model* iItemModel;
1432 sink iItemModelSink;
1435 container_type iRows;
1436 mutable row_map_type iRowMap;
1438 mutable column_info_array iColumns;
1439 mutable column_map_type iColumnMap;
1440 mutable optional_font iDefaultFont;
1441 mutable std::optional<i_scrollbar::value_type> iTotalHeight;
1443 bool iAlternatingRowColor;
1444 std::deque<sort_by_param> iSortOrder;
1445 std::vector<filter> iFilters;
1447 std::uint32_t iUpdating = 0u;
1448 bool iFiltering =
false;