/* SPDX-FileCopyrightText: 2023 Blender Authors * * SPDX-License-Identifier: GPL-2.0-or-later */ #include #include #include #include "BLI_math_matrix.hh" #include "BLI_math_quaternion_types.hh" #include "BLI_math_vector_types.hh" #include "BKE_instances.hh" #include "spreadsheet_column_values.hh" #include "spreadsheet_data_source_geometry.hh" #include "spreadsheet_layout.hh" #include "DNA_meshdata_types.h" #include "UI_interface.hh" #include "UI_resources.hh" #include "BLT_translation.hh" namespace blender::ed::spreadsheet { class SpreadsheetLayoutDrawer : public SpreadsheetDrawer { private: const SpreadsheetLayout &spreadsheet_layout_; public: SpreadsheetLayoutDrawer(const SpreadsheetLayout &spreadsheet_layout) : spreadsheet_layout_(spreadsheet_layout) { tot_columns = spreadsheet_layout.columns.size(); tot_rows = spreadsheet_layout.row_indices.size(); left_column_width = spreadsheet_layout.index_column_width; } void draw_top_row_cell(int column_index, const CellDrawParams ¶ms) const final { const StringRefNull name = spreadsheet_layout_.columns[column_index].values->name(); uiBut *but = uiDefIconTextBut(params.block, UI_BTYPE_LABEL, 0, ICON_NONE, name, params.xmin, params.ymin, params.width, params.height, nullptr, 0, 0, nullptr); /* Center-align column headers. */ UI_but_drawflag_disable(but, UI_BUT_TEXT_LEFT); UI_but_drawflag_disable(but, UI_BUT_TEXT_RIGHT); } void draw_left_column_cell(int row_index, const CellDrawParams ¶ms) const final { const int real_index = spreadsheet_layout_.row_indices[row_index]; std::string index_str = std::to_string(real_index); uiBut *but = uiDefIconTextBut(params.block, UI_BTYPE_LABEL, 0, ICON_NONE, index_str, params.xmin, params.ymin, params.width, params.height, nullptr, 0, 0, nullptr); /* Right-align indices. */ UI_but_drawflag_enable(but, UI_BUT_TEXT_RIGHT); UI_but_drawflag_disable(but, UI_BUT_TEXT_LEFT); } void draw_content_cell(int row_index, int column_index, const CellDrawParams ¶ms) const final { const int real_index = spreadsheet_layout_.row_indices[row_index]; const ColumnValues &column = *spreadsheet_layout_.columns[column_index].values; if (real_index > column.size()) { return; } const GVArray &data = column.data(); if (data.type().is()) { const int value = data.get(real_index); const std::string value_str = std::to_string(value); uiBut *but = uiDefIconTextBut(params.block, UI_BTYPE_LABEL, 0, ICON_NONE, value_str, params.xmin, params.ymin, params.width, params.height, nullptr, 0, 0, nullptr); UI_but_func_tooltip_set( but, [](bContext * /*C*/, void *argN, const char * /*tip*/) { return fmt::format("{}", *((int *)argN)); }, MEM_cnew(__func__, value), MEM_freeN); /* Right-align Integers. */ UI_but_drawflag_disable(but, UI_BUT_TEXT_LEFT); UI_but_drawflag_enable(but, UI_BUT_TEXT_RIGHT); } if (data.type().is()) { const int8_t value = data.get(real_index); const std::string value_str = std::to_string(value); uiBut *but = uiDefIconTextBut(params.block, UI_BTYPE_LABEL, 0, ICON_NONE, value_str, params.xmin, params.ymin, params.width, params.height, nullptr, 0, 0, nullptr); /* Right-align Integers. */ UI_but_drawflag_disable(but, UI_BUT_TEXT_LEFT); UI_but_drawflag_enable(but, UI_BUT_TEXT_RIGHT); } else if (data.type().is()) { const int2 value = int2(data.get(real_index)); this->draw_int_vector(params, Span(&value.x, 2)); } else if (data.type().is()) { const int2 value = data.get(real_index); this->draw_int_vector(params, Span(&value.x, 2)); } else if (data.type().is()) { const float value = data.get(real_index); std::stringstream ss; ss << std::fixed << std::setprecision(3) << value; const std::string value_str = ss.str(); uiBut *but = uiDefIconTextBut(params.block, UI_BTYPE_LABEL, 0, ICON_NONE, value_str, params.xmin, params.ymin, params.width, params.height, nullptr, 0, 0, nullptr); UI_but_func_tooltip_set( but, [](bContext * /*C*/, void *argN, const char * /*tip*/) { return fmt::format("{:f}", *((float *)argN)); }, MEM_cnew(__func__, value), MEM_freeN); /* Right-align Floats. */ UI_but_drawflag_disable(but, UI_BUT_TEXT_LEFT); UI_but_drawflag_enable(but, UI_BUT_TEXT_RIGHT); } else if (data.type().is()) { const bool value = data.get(real_index); const int icon = value ? ICON_CHECKBOX_HLT : ICON_CHECKBOX_DEHLT; uiBut *but = uiDefIconTextBut(params.block, UI_BTYPE_LABEL, 0, icon, "", params.xmin, params.ymin, params.width, params.height, nullptr, 0, 0, nullptr); UI_but_drawflag_disable(but, UI_BUT_ICON_LEFT); } else if (data.type().is()) { const float2 value = data.get(real_index); this->draw_float_vector(params, Span(&value.x, 2)); } else if (data.type().is()) { const float3 value = data.get(real_index); this->draw_float_vector(params, Span(&value.x, 3)); } else if (data.type().is()) { const ColorGeometry4f value = data.get(real_index); this->draw_float_vector(params, Span(&value.r, 4)); } else if (data.type().is()) { const ColorGeometry4b value = data.get(real_index); this->draw_byte_color(params, value); } else if (data.type().is()) { const float4 value = float4(data.get(real_index)); this->draw_float_vector(params, Span(&value.x, 4)); } else if (data.type().is()) { this->draw_float4x4(params, data.get(real_index)); } else if (data.type().is()) { const bke::InstanceReference value = data.get(real_index); const StringRefNull name = value.name().is_empty() ? IFACE_("(Geometry)") : value.name(); const int icon = get_instance_reference_icon(value); uiDefIconTextBut(params.block, UI_BTYPE_LABEL, 0, icon, name.c_str(), params.xmin, params.ymin, params.width, params.height, nullptr, 0, 0, nullptr); } else if (data.type().is()) { uiDefIconTextBut(params.block, UI_BTYPE_LABEL, 0, ICON_NONE, data.get(real_index), params.xmin, params.ymin, params.width, params.height, nullptr, 0, 0, nullptr); } else if (data.type().is()) { MStringProperty *prop = MEM_cnew(__func__); data.get_to_uninitialized(real_index, prop); uiBut *but = uiDefIconTextBut(params.block, UI_BTYPE_LABEL, 0, ICON_NONE, StringRef(prop->s, prop->s_len), params.xmin, params.ymin, params.width, params.height, nullptr, 0, 0, nullptr); UI_but_func_tooltip_set( but, [](bContext * /*C*/, void *argN, const char * /*tip*/) { const MStringProperty &prop = *static_cast(argN); return std::string(StringRef(prop.s, prop.s_len)); }, prop, MEM_freeN); } } void draw_float_vector(const CellDrawParams ¶ms, const Span values) const { BLI_assert(!values.is_empty()); const float segment_width = float(params.width) / values.size(); for (const int i : values.index_range()) { std::stringstream ss; const float value = values[i]; ss << " " << std::fixed << std::setprecision(3) << value; const std::string value_str = ss.str(); uiBut *but = uiDefIconTextBut(params.block, UI_BTYPE_LABEL, 0, ICON_NONE, value_str, params.xmin + i * segment_width, params.ymin, segment_width, params.height, nullptr, 0, 0, nullptr); UI_but_func_tooltip_set( but, [](bContext * /*C*/, void *argN, const char * /*tip*/) { return fmt::format("{:f}", *((float *)argN)); }, MEM_cnew(__func__, value), MEM_freeN); /* Right-align Floats. */ UI_but_drawflag_disable(but, UI_BUT_TEXT_LEFT); UI_but_drawflag_enable(but, UI_BUT_TEXT_RIGHT); } } void draw_int_vector(const CellDrawParams ¶ms, const Span values) const { BLI_assert(!values.is_empty()); const float segment_width = float(params.width) / values.size(); for (const int i : values.index_range()) { std::stringstream ss; const int value = values[i]; ss << " " << value; const std::string value_str = ss.str(); uiBut *but = uiDefIconTextBut(params.block, UI_BTYPE_LABEL, 0, ICON_NONE, value_str, params.xmin + i * segment_width, params.ymin, segment_width, params.height, nullptr, 0, 0, nullptr); UI_but_func_tooltip_set( but, [](bContext * /*C*/, void *argN, const char * /*tip*/) { return fmt::format("{}", *((int *)argN)); }, MEM_cnew(__func__, value), MEM_freeN); /* Right-align Floats. */ UI_but_drawflag_disable(but, UI_BUT_TEXT_LEFT); UI_but_drawflag_enable(but, UI_BUT_TEXT_RIGHT); } } void draw_byte_color(const CellDrawParams ¶ms, const ColorGeometry4b color) const { const ColorGeometry4f float_color = color.decode(); Span values(&float_color.r, 4); const float segment_width = float(params.width) / values.size(); for (const int i : values.index_range()) { std::stringstream ss; const float value = values[i]; ss << " " << std::fixed << std::setprecision(3) << value; const std::string value_str = ss.str(); uiBut *but = uiDefIconTextBut(params.block, UI_BTYPE_LABEL, 0, ICON_NONE, value_str, params.xmin + i * segment_width, params.ymin, segment_width, params.height, nullptr, 0, 0, nullptr); /* Right-align Floats. */ UI_but_drawflag_disable(but, UI_BUT_TEXT_LEFT); UI_but_drawflag_enable(but, UI_BUT_TEXT_RIGHT); /* Tooltip showing raw byte values. Encode values in pointer to avoid memory allocation. */ UI_but_func_tooltip_set( but, [](bContext * /*C*/, void *argN, const char * /*tip*/) { const uint32_t uint_color = POINTER_AS_UINT(argN); ColorGeometry4b color = *(ColorGeometry4b *)&uint_color; return fmt::format(fmt::runtime(TIP_("Byte Color (sRGB encoded):\n{} {} {} {}")), color.r, color.g, color.b, color.a); }, POINTER_FROM_UINT(*(uint32_t *)&color), nullptr); } } void draw_float4x4(const CellDrawParams ¶ms, const float4x4 &value) const { uiBut *but = uiDefIconTextBut(params.block, UI_BTYPE_LABEL, 0, ICON_NONE, "...", params.xmin, params.ymin, params.width, params.height, nullptr, 0, 0, nullptr); /* Center alignment. */ UI_but_drawflag_disable(but, UI_BUT_TEXT_LEFT); UI_but_func_tooltip_set( but, [](bContext * /*C*/, void *argN, const char * /*tip*/) { /* Transpose to be able to print row by row. */ const float4x4 value = math::transpose(*static_cast(argN)); std::stringstream ss; ss << value[0] << ",\n"; ss << value[1] << ",\n"; ss << value[2] << ",\n"; ss << value[3]; return ss.str(); }, MEM_cnew(__func__, value), MEM_freeN); } int column_width(int column_index) const final { return spreadsheet_layout_.columns[column_index].width; } }; std::unique_ptr spreadsheet_drawer_from_layout( const SpreadsheetLayout &spreadsheet_layout) { return std::make_unique(spreadsheet_layout); } } // namespace blender::ed::spreadsheet