diff options
| -rw-r--r-- | .gitignore | 1 | ||||
| -rw-r--r-- | CMakeLists.txt | 55 | ||||
| -rw-r--r-- | boost_any_to_qvariant.hpp | 67 | ||||
| -rw-r--r-- | friendly_fusion.hpp | 129 | ||||
| -rw-r--r-- | fusion_model.hpp | 52 | ||||
| -rw-r--r-- | fusion_static_dispatch.hpp | 127 | ||||
| -rw-r--r-- | gui_item_delegate.cpp | 21 | ||||
| -rw-r--r-- | gui_item_delegate.hpp | 18 | ||||
| -rw-r--r-- | index_list.hpp | 16 | ||||
| -rw-r--r-- | main.cpp | 75 | ||||
| -rw-r--r-- | meta_types.hpp | 5 | ||||
| -rw-r--r-- | qt_adapter.hpp | 69 |
12 files changed, 635 insertions, 0 deletions
@@ -1 +1,2 @@ *build* +*.txt.user diff --git a/CMakeLists.txt b/CMakeLists.txt index a6c35f3..1d5e0d4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,6 +3,61 @@ project(msc-scriptie) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --std=c++11") +function(MY_QT5_WRAP_UI outfiles ) + set(options) + set(oneValueArgs) + set(multiValueArgs OPTIONS) + + cmake_parse_arguments(_WRAP_UI "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + set(ui_files ${_WRAP_UI_UNPARSED_ARGUMENTS}) + set(ui_options ${_WRAP_UI_OPTIONS}) + + foreach(it ${ui_files}) + get_filename_component(outfile ${it} NAME_WE) + get_filename_component(infile ${it} ABSOLUTE) + set(outfile ${CMAKE_CURRENT_SOURCE_DIR}/ui_${outfile}.hpp) + add_custom_command(OUTPUT ${outfile} + COMMAND ${Qt5Widgets_UIC_EXECUTABLE} + ARGS ${ui_options} -o ${outfile} ${infile} + MAIN_DEPENDENCY ${infile} + VERBATIM) + list(APPEND ${outfiles} ${outfile}) + endforeach() + set(${outfiles} ${${outfiles}} PARENT_SCOPE) +endfunction() + +find_package(Qt5Widgets REQUIRED) +add_definitions(${Qt5Widgets_DEFINITIONS}) +include_directories(SYSTEM ${Qt5Widgets_INCLUDE_DIRS}) + +find_package(Qt5Gui REQUIRED) +add_definitions(${Qt5Gui_DEFINITIONS}) +include_directories(SYSTEM ${Qt5Gui_INCLUDE_DIRS}) + +find_package(Qt5Core REQUIRED) +add_definitions(${Qt5Core_DEFINITIONS}) +include_directories(SYSTEM ${Qt5Core_INCLUDE_DIRS}) + +qt5_wrap_cpp(qt_sources + gui_item_delegate.hpp +) + add_executable(msc-scriptie + fusion_model.hpp + index_list.hpp + qt_adapter.hpp + friendly_fusion.hpp + fusion_static_dispatch.hpp + meta_types.hpp + boost_any_to_qvariant.hpp + gui_item_delegate.hpp gui_item_delegate.cpp + ${qt_sources} main.cpp ) + +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${Qt5Widgets_EXECUTABLE_COMPILE_FLAGS} ${Qt5Gui_EXECUTABLE_COMPILE_FLAGS} ${Qt5Core_EXECUTABLE_COMPILE_FLAGS}") +target_link_libraries(msc-scriptie ${Qt5Widgets_LIBRARIES} ${Qt5Gui_LIBRARIES} ${Qt5Core_LIBRARIES}) + + + diff --git a/boost_any_to_qvariant.hpp b/boost_any_to_qvariant.hpp new file mode 100644 index 0000000..6172c09 --- /dev/null +++ b/boost_any_to_qvariant.hpp @@ -0,0 +1,67 @@ +#pragma once + +#include "friendly_fusion.hpp" +#include "index_list.hpp" + +#include <QVariant> + +#include <boost/any.hpp> + +#include <string> + +template<typename value_type> +QVariant convert(boost::any const& x) +{ + return QVariant::fromValue<value_type>(boost::any_cast<value_type>(x)); +} + +template <typename T, int n> +QVariant convert(boost::any const& x) +{ + typedef friendly_fusion::result_of::begin<T> begin; + typedef friendly_fusion::result_of::advance_c<typename begin::type, n> adv_it; + typedef friendly_fusion::result_of::deref<typename adv_it::type> deref; + typedef typename std::decay<typename deref::type>::type value_type; + return convert<value_type>(x); +} + +#if defined( __GNUC__ ) && !defined( __clang__ ) +template <typename T, int index> +std::function<QVariant(boost::any const&)> convert_lambda() +{ + return [](boost::any const& any) + { + return convert<T, index>(any); + }; +} + +#endif //defined( __GNUC__ ) && !defined( __clang__ ) + +template<typename T, int... Indices> +QVariant to_qvariant(boost::any const& any, int index, indices<Indices...>) +{ + typedef std::function<QVariant(boost::any const&)> element_type; + static element_type table[] = + { +#if defined( __GNUC__ ) && !defined( __clang__ ) + //Workaround for gcc bug: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=47226 + convert_lambda<T, Indices>() + ... +#else + [](boost::any const& any){return convert<T, Indices>(any);} + ... +#endif //defined( __GNUC__ ) && !defined( __clang__ ) + }; + + return table[index](any); +} + + +template <typename T> +QVariant to_qvariant(boost::any const& x, int index) +{ + typedef typename friendly_fusion::result_of::size<T>::type seq_size; + typedef typename build_indices<seq_size::value>::type indices_type; + + return to_qvariant<T>(x, index, indices_type{}); +}
\ No newline at end of file diff --git a/friendly_fusion.hpp b/friendly_fusion.hpp new file mode 100644 index 0000000..b014bd8 --- /dev/null +++ b/friendly_fusion.hpp @@ -0,0 +1,129 @@ +#pragma once + +#include <boost/fusion/include/size.hpp> +#include <boost/fusion/include/begin.hpp> +#include <boost/fusion/include/deref.hpp> +#include <boost/fusion/include/value_of.hpp> +#include <boost/fusion/include/distance.hpp> +#include <boost/fusion/include/advance.hpp> +#include <boost/fusion/include/is_view.hpp> +#include <boost/fusion/include/is_sequence.hpp> +#include <boost/fusion/include/is_iterator.hpp> +#include <boost/fusion/include/struct.hpp> +#include <boost/fusion/adapted/struct/detail/adapt_base.hpp> + +#include <boost/mpl/or.hpp> + +namespace friendly_fusion { + +namespace traits { + +template <typename T, bool NoCheck = true> +struct is_sequence : public boost::fusion::traits::is_sequence<T> +{ + static_assert(NoCheck || boost::fusion::traits::is_sequence<T>::value, "Template parameter: T, is not a fusion adapted struct."); +}; + +template <typename T, bool NoCheck = true> +struct is_fusion_iterator : public boost::fusion::is_fusion_iterator<T> +{ + static_assert(NoCheck || boost::fusion::is_fusion_iterator<T>::value, "Template parameter: T. Is not a fusion iterator."); +}; + +template <typename T, bool NoCheck = true> +struct is_view : public boost::fusion::traits::is_view<T> { + static_assert(NoCheck || boost::fusion::traits::is_view<T>::value, "Template parameter: T, is not a fusion view"); +}; + +template <typename T, bool NoCheck = true> +struct is_sequence_or_view : public boost::mpl::or_<typename boost::fusion::traits::is_sequence<T>, typename boost::fusion::traits::is_view<T>> { + static_assert(NoCheck || (boost::fusion::traits::is_sequence<T>::value || boost::fusion::traits::is_view<T>::value), "Template parameter: T, is not a fusion sequence or a view"); +}; + +} + +template <typename T> +typename std::enable_if<traits::is_fusion_iterator<T, false>::value, typename boost::fusion::result_of::deref<T>::type>::type +deref(T const& x) +{ + return boost::fusion::deref(x); +} + +template <int N, typename T> +typename std::enable_if<traits::is_fusion_iterator<T, false>::value, typename boost::fusion::result_of::advance_c<T, N>::type>::type +advance_c(T const& x) +{ + return boost::fusion::advance_c<N>(x); +} + +template <typename T> +typename std::enable_if<traits::is_sequence_or_view<T, false>::value, typename boost::fusion::result_of::begin<T>::type>::type const +begin(T& x) +{ + return boost::fusion::begin(x); +} + +template <typename T> +typename std::enable_if<traits::is_sequence_or_view<T, false>::value, typename boost::fusion::result_of::begin<T const>::type>::type const +begin(T const& x) +{ + return boost::fusion::begin(x); +} + +template <typename T> +typename std::enable_if<traits::is_sequence_or_view<T, false>::value, typename boost::fusion::result_of::end<T>::type>::type const +end(T& x) +{ + return boost::fusion::end(x); +} + +template <typename T> +typename std::enable_if<traits::is_sequence_or_view<T, false>::value, typename boost::fusion::result_of::end<T const>::type>::type const +end(T const& x) +{ + return boost::fusion::end(x); +} + +namespace result_of { + +template <typename T> +struct size : private traits::is_sequence_or_view<T, false>, public boost::fusion::result_of::size<T> +{ + using typename boost::fusion::result_of::size<T>::type; +}; + +template <typename T> +struct begin : private traits::is_sequence_or_view<T, false>, public boost::fusion::result_of::begin<T> +{ + using typename boost::fusion::result_of::begin<T>::type; +}; + +template <typename T> +struct end : private traits::is_sequence_or_view<T, false>, public boost::fusion::result_of::end<T> +{ + using typename boost::fusion::result_of::end<T>::type; +}; + +template <typename T, int N> +struct advance_c : private traits::is_fusion_iterator<T, false>, public boost::fusion::result_of::advance_c<T, N> +{ + using typename boost::fusion::result_of::advance_c<T, N>::type; +}; + +template <typename T> +struct deref : private traits::is_fusion_iterator<T, false>, public boost::fusion::result_of::deref<T> +{ + using typename boost::fusion::result_of::deref<T>::type; +}; + +} + +namespace extension { + +template<typename T, int index> +struct struct_member_name : private traits::is_sequence<T, false>, public boost::fusion::extension::struct_member_name<T, index> +{}; + +} + +}
\ No newline at end of file diff --git a/fusion_model.hpp b/fusion_model.hpp new file mode 100644 index 0000000..ace8f51 --- /dev/null +++ b/fusion_model.hpp @@ -0,0 +1,52 @@ +#pragma once + +#include "fusion_static_dispatch.hpp" + +#include <boost/fusion/include/is_sequence.hpp> +#include <boost/fusion/include/size.hpp> + +#include <boost/any.hpp> + +#include <vector> +#include <map> + +template <typename T> +struct fusion_model { + +}; + +template <typename T> +struct fusion_model<std::vector<T>> +{ + static_assert(boost::fusion::traits::is_sequence<T>::type::value, "T is not a boost fusion sequence"); + + typedef std::vector<T> data_type; + + std::vector<T> data; + + size_t row_count() const + { + return data.size(); + } + + size_t column_count() const + { + return boost::fusion::result_of::size<T>::type::value; + } + + std::string horizontal_header_data(size_t section) const + { + return get_nth_name<T>(section); + } + + boost::any get_cell(size_t row, size_t column) const + { + return get_nth(data[row], column); + } +}; + +template <typename T, typename U> +struct fusion_model<std::map<T, U>> +{ + std::map<T, U> data; +};
\ No newline at end of file diff --git a/fusion_static_dispatch.hpp b/fusion_static_dispatch.hpp new file mode 100644 index 0000000..17b6ff4 --- /dev/null +++ b/fusion_static_dispatch.hpp @@ -0,0 +1,127 @@ +#pragma once + +#include "index_list.hpp" +#include "friendly_fusion.hpp" + +#include <boost/any.hpp> + +template <typename T, int index> +struct TypeAt { + typedef friendly_fusion::result_of::begin<T> begin; + typedef friendly_fusion::result_of::advance_c<typename begin::type, index> adv_it; + typedef friendly_fusion::result_of::deref<typename adv_it::type> deref; + typedef typename std::decay<typename deref::type>::type value_type; +}; + +#if defined( __GNUC__ ) && !defined( __clang__ ) +template<int index, typename T> +std::function<boost::any(T)> make_at_c_lambda(T seq) +{ + return [](T seq){ + return boost::any(friendly_fusion::deref(friendly_fusion::advance_c<index>(friendly_fusion::begin(seq)))); + }; +} +#endif //defined( __GNUC__ ) && !defined( __clang__ ) + +template<typename T, int... Indices> +boost::any get_nth_impl(T seq, int index, indices<Indices...>) +{ + typedef std::function<boost::any(T)> element_type; + static element_type table[] = + { +#if defined( __GNUC__ ) && !defined( __clang__ ) + //Workaround for gcc bug: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=47226 + make_at_c_lambda<Indices>(seq) + ... +#else + [](T seq){return boost::any(friendly_fusion::deref(friendly_fusion::advance_c<Indices>(friendly_fusion::begin(seq))));} + ... +#endif //defined( __GNUC__ ) && !defined( __clang__ ) + }; + + return table[index](seq); +} + +template<typename T> +struct get_nth_functor +{ + boost::any operator()(T seq, int index) + { + typedef typename friendly_fusion::result_of::size<T>::type seq_size; + typedef typename build_indices<seq_size::value>::type indices_type; + + return get_nth_impl(seq, index, indices_type{}); + } +}; + +template <typename T> +boost::any get_nth(T x, int index) +{ + return get_nth_functor<T>()(x, index); +} + +#if defined( __GNUC__ ) && !defined( __clang__ ) +template<int index, typename T> +std::function<std::string()> make_struct_member_name_lambda() +{ + return []{ + return std::string(friendly_fusion::extension::struct_member_name<T, index>::call()); + }; +} +#endif //defined( __GNUC__ ) && !defined( __clang__ ) + +template<typename T> +struct get_nth_name_impl { + + get_nth_name_impl() = default; + + template<int... Indices> + std::string operator()(int index, indices<Indices...>) + { + typedef std::function<std::string()> element_type; + static element_type table[] = { + #if defined( __GNUC__ ) && !defined( __clang__ ) + //Workaround for gcc bug: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=47226 + make_struct_member_name_lambda<Indices, T>() + ... + #else + []{return std::string(friendly_fusion::extension::struct_member_name<T, Indices>::call());} + ... + #endif //defined( __GNUC__ ) && !defined( __clang__ ) + }; + + return table[index](); + } +}; + +template <typename T> +struct get_nth_name_functor +{ + std::string operator()(int index) + { + typedef typename friendly_fusion::result_of::size<T>::type seq_size; + typedef typename build_indices<seq_size::value>::type indices_type; + + return get_nth_name_impl<T>()(index, indices_type{}); + } +}; + +template <typename T, typename U> +struct get_nth_name_functor<boost::fusion::joint_view<T,U>> +{ + std::string operator()(int index) + { + constexpr size_t size_of_T = friendly_fusion::result_of::size<T>::type::value; + if(index < size_of_T){ + return get_nth_name_functor<T>()(index); + } else { + return get_nth_name_functor<U>()(index - size_of_T); + } + } +}; + +template <typename T> +std::string get_nth_name(int index) +{ + return get_nth_name_functor<T>()(index); +} diff --git a/gui_item_delegate.cpp b/gui_item_delegate.cpp new file mode 100644 index 0000000..aba0727 --- /dev/null +++ b/gui_item_delegate.cpp @@ -0,0 +1,21 @@ +#include "meta_types.hpp" + +#include "gui_item_delegate.hpp" + +GuiItemDelegate::GuiItemDelegate(QObject *parent) : +QStyledItemDelegate(parent) +{ +} + +QString GuiItemDelegate::displayText(const QVariant &value, const QLocale &locale) const +{ + QString ret; + static int string_id = qMetaTypeId<std::string>(); + + if(string_id == value.userType()) { + ret = QString::fromStdString(value.value<std::string>()); + } else { + ret = QStyledItemDelegate::displayText(value, locale); + } + return ret; +} diff --git a/gui_item_delegate.hpp b/gui_item_delegate.hpp new file mode 100644 index 0000000..d715dc2 --- /dev/null +++ b/gui_item_delegate.hpp @@ -0,0 +1,18 @@ +#pragma once + +#include <QStyledItemDelegate> + +class GuiItemDelegate : public QStyledItemDelegate +{ + Q_OBJECT +public: + explicit GuiItemDelegate(QObject *parent = 0); + virtual ~GuiItemDelegate() = default; + + virtual QString displayText(const QVariant &value, const QLocale &locale) const override final; + +signals: + +public slots: + +}; diff --git a/index_list.hpp b/index_list.hpp new file mode 100644 index 0000000..f363dc3 --- /dev/null +++ b/index_list.hpp @@ -0,0 +1,16 @@ +#pragma once + +template<int... Indices> +struct indices { + typedef indices<Indices..., sizeof...(Indices)> next; +}; + +template<int N> +struct build_indices { + typedef typename build_indices<N - 1>::type::next type; +}; + +template<> +struct build_indices<0> { + typedef indices<> type; +}; @@ -1,5 +1,80 @@ +#include "fusion_model.hpp" +#include "qt_adapter.hpp" +#include "gui_item_delegate.hpp" + +#include <boost/fusion/adapted.hpp> + +#include <iostream> + +#include <QMainWindow> +#include <QApplication> + +struct Data { + std::string name; + uint32_t number; + float ratio; + bool lolwut; +}; + +BOOST_FUSION_ADAPT_STRUCT( + Data, + (std::string, name) + (uint32_t, number) + (float, ratio) + (bool, lolwut) +) + +struct DataModel : public fusion_model<std::vector<Data>> { + + void add_data(Data d) { + data.push_back(d); + } +}; + int main() { + DataModel model; + + model.add_data({"Pietje", 2, 3.333f, true}); + model.add_data({"Jantje", 3, 1.5f, false}); + model.add_data({"Sjaakje", 1, 0.1337f, false}); + + auto adapter = make_qt_adapter(model); + + int argc = 0; + QApplication qapp(argc, nullptr); + + QMainWindow w; + + decltype(adapter)::element_type::view view; + + view.setModel(adapter.get()); + + GuiItemDelegate delegate; + view.setItemDelegate(&delegate); + + view.show(); + + qapp.exec(); + + for(int column = 0; column < model.column_count(); ++column) + { + std::cout << model.horizontal_header_data(column) << "\t"; + } + std::cout << std::endl; + + for(int row = 0; row < model.row_count(); ++row) + { + for(int column = 0; column < model.column_count(); ++column) { + if(column == 0) { + std::cout << boost::any_cast<std::string>(model.get_cell(row, column)) << "\t"; + } else { + std::cout << boost::any_cast<uint32_t>(model.get_cell(row, column)) << "\t"; + } + } + std::cout << std::endl; + } + return 0; } diff --git a/meta_types.hpp b/meta_types.hpp new file mode 100644 index 0000000..265d9e9 --- /dev/null +++ b/meta_types.hpp @@ -0,0 +1,5 @@ +#pragma once + +#include <QMetaType> + +Q_DECLARE_METATYPE(std::string)
\ No newline at end of file diff --git a/qt_adapter.hpp b/qt_adapter.hpp new file mode 100644 index 0000000..2843667 --- /dev/null +++ b/qt_adapter.hpp @@ -0,0 +1,69 @@ +#pragma once + +#include "fusion_model.hpp" +#include "boost_any_to_qvariant.hpp" +#include "meta_types.hpp" + +#include <QAbstractTableModel> +#include <QTableView> +#include <QMetaType> + +#include <memory> + + + +template <typename T> +struct QtModelType +{ + typedef void type; +}; + +template <typename V> +struct QtModelType<std::vector<V>> +{ + typedef QAbstractTableModel type; +}; + +template <typename T, typename Q> +struct QtAdapter : public Q { + + typedef void view; + + QtAdapter(T) + {} +}; + +template <typename T> +struct QtAdapter<T, QAbstractTableModel> : public QAbstractTableModel +{ + typedef QTableView view; + + T model; + + QtAdapter(T model) + : model(model) + {} + + virtual int rowCount(QModelIndex const&) const override + { + return model.row_count(); + } + + virtual int columnCount(QModelIndex const&) const override + { + return model.column_count(); + } + + virtual QVariant data(QModelIndex const& index, int role) const + { + if(role != Qt::DisplayRole) return QVariant(); + + return to_qvariant<typename T::data_type::value_type>(model.get_cell(index.row(), index.column()), index.column()); + } +}; + + +template <typename T> +std::shared_ptr<QtAdapter<T, typename QtModelType<typename T::data_type>::type>> make_qt_adapter(T value) { + return std::make_shared<QtAdapter<T, typename QtModelType<typename T::data_type>::type>>(value); +} |
