add listbox model feature

This commit is contained in:
Jinhao 2016-04-09 04:13:22 +08:00
parent 8ec21827a0
commit a27a85d0c9
3 changed files with 1420 additions and 485 deletions

View File

@ -26,6 +26,8 @@
#include <nana/key_type.hpp> #include <nana/key_type.hpp>
#include <functional> #include <functional>
#include <initializer_list> #include <initializer_list>
#include <mutex>
#include <typeinfo>
namespace nana namespace nana
{ {
@ -35,6 +37,543 @@ namespace nana
{ {
namespace listbox namespace listbox
{ {
class const_virtual_pointer
{
struct intern
{
public:
virtual ~intern() = default;
};
template<typename T>
struct real_pointer
: public intern
{
const T * ptr;
real_pointer(const T* p)
: ptr(p)
{}
};
const_virtual_pointer(const const_virtual_pointer&) = delete;
const_virtual_pointer& operator=(const const_virtual_pointer&) = delete;
const_virtual_pointer(const_virtual_pointer&&) = delete;
const_virtual_pointer& operator=(const_virtual_pointer&&) = delete;
public:
template<typename Type>
explicit const_virtual_pointer(const Type* p)
: intern_(new real_pointer<Type>{p})
{
}
~const_virtual_pointer()
{
delete intern_;
}
template<typename Type>
const typename std::remove_const<Type>::type *get()
{
using value_type = typename std::remove_const<Type>::type;
auto target = dynamic_cast<real_pointer<value_type>*>(intern_);
return (target ? target->ptr : nullptr);
}
private:
intern * intern_;
};
struct cell
{
struct format
{
::nana::color bgcolor;
::nana::color fgcolor;
format() = default;
format(const ::nana::color& bgcolor, const ::nana::color& fgcolor);
};
using format_ptr = ::std::unique_ptr<format>;
::std::string text;
format_ptr custom_format;
cell() = default;
cell(const cell&);
cell(cell&&);
cell(::std::string);
cell(::std::string, const format&);
cell(::std::string, const ::nana::color& bgcolor, const ::nana::color& fgcolor);
cell& operator=(const cell&);
cell& operator=(cell&&);
};
class container_interface
{
friend class model_guard;
public:
virtual ~container_interface() = default;
virtual void clear() = 0;
virtual void erase(std::size_t pos) = 0;
virtual std::size_t size() const = 0;
virtual bool immutable() const = 0;
virtual void emplace(std::size_t pos) = 0;
virtual void emplace_back() = 0;
virtual void assign(std::size_t pos, const std::vector<cell>& cells) = 0;
virtual std::vector<cell> to_cells(std::size_t pos) const = 0;
virtual bool push_back(const_virtual_pointer&) = 0;
virtual void * pointer() = 0;
virtual const void* pointer() const = 0;
};
template<typename Value>
struct container_translator
{
using value_translator = std::function<Value(const std::vector<cell>& cells)>;
using cell_translator = std::function<std::vector<cell>(const Value&)>;
value_translator to_value;
cell_translator to_cell;
};
template<typename STLContainer>
class basic_container
: public container_interface
{
};
template<typename STLContainer>
class standalone_container
: public basic_container<STLContainer>
{
using value_type = typename STLContainer::value_type;
using value_translator = typename container_translator<value_type>::value_translator;
using cell_translator = typename container_translator<value_type>::cell_translator;
public:
standalone_container(STLContainer&& cont, value_translator vtrans, cell_translator ctrans)
: container_(std::move(cont)),
translator_({ vtrans, ctrans })
{}
standalone_container(const STLContainer& cont, value_translator vtrans, cell_translator ctrans)
: container_(cont),
translator_({ vtrans, ctrans })
{}
private:
void clear() override
{
container_.clear();
}
void erase(std::size_t pos) override
{
auto i = container_.begin();
std::advance(i, static_cast<int>(pos));
container_.erase(i);
}
std::size_t size() const override
{
return container_.size();
}
bool immutable() const override
{
return false;
}
void emplace(std::size_t pos) override
{
auto i = container_.begin();
std::advance(i, static_cast<int>(pos));
container_.emplace(i);
}
void emplace_back() override
{
container_.emplace_back();
}
void assign(std::size_t pos, const std::vector<cell>& cells) override
{
container_.at(pos) = translator_.to_value(cells);
}
std::vector<cell> to_cells(std::size_t pos) const override
{
return translator_.to_cell(container_.at(pos));
}
bool push_back(const_virtual_pointer& dptr) override
{
auto value = dptr.get<value_type>();
if (value)
{
container_.push_back(*value);
return true;
}
return false;
}
void* pointer() override
{
return &container_;
}
const void* pointer() const override
{
return &container_;
}
private:
STLContainer container_;
container_translator<value_type> translator_;
};
template<typename STLContainer>
class shared_container
: public basic_container<STLContainer>
{
using value_type = typename STLContainer::value_type;
using value_translator = typename container_translator<value_type>::value_translator;
using cell_translator = typename container_translator<value_type>::cell_translator;
public:
using container_reference = STLContainer&;
shared_container(container_reference cont, value_translator vtrans, cell_translator ctrans)
: container_(cont), translator_({ vtrans, ctrans })
{
}
private:
void clear() override
{
container_.clear();
}
void erase(std::size_t pos) override
{
auto i = container_.begin();
std::advance(i, static_cast<int>(pos));
container_.erase(i);
}
std::size_t size() const override
{
return container_.size();
}
bool immutable() const override
{
return false;
}
void emplace(std::size_t pos) override
{
auto i = container_.begin();
std::advance(i, static_cast<int>(pos));
container_.emplace(i);
}
void emplace_back() override
{
container_.emplace_back();
}
void assign(std::size_t pos, const std::vector<cell>& cells) override
{
container_.at(pos) = translator_.to_value(cells);
}
std::vector<cell> to_cells(std::size_t pos) const override
{
return translator_.to_cell(container_.at(pos));
}
bool push_back(const_virtual_pointer& dptr) override
{
auto value = dptr.get<value_type>();
if (value)
{
container_.push_back(*value);
return true;
}
return false;
}
void* pointer() override
{
return &container_;
}
const void* pointer() const override
{
return &container_;
}
private:
container_reference container_;
container_translator<value_type> translator_;
};
template<typename STLContainer>
class shared_immutable_container
: public basic_container<STLContainer>
{
using value_type = typename STLContainer::value_type;
using cell_translator = typename container_translator<value_type>::cell_translator;
public:
using container_reference = const STLContainer&;
shared_immutable_container(container_reference cont, cell_translator ctrans)
: container_(cont), ctrans_(ctrans)
{
}
private:
void clear() override
{
throw std::runtime_error("nana::listbox disallow to remove items because of immutable model");
}
void erase(std::size_t pos) override
{
throw std::runtime_error("nana::listbox disallow to remove items because of immutable model");
}
std::size_t size() const override
{
return container_.size();
}
bool immutable() const override
{
return true;
}
void emplace(std::size_t pos) override
{
throw std::runtime_error("nana::listbox disallow to remove items because of immutable model");
}
void emplace_back() override
{
throw std::runtime_error("nana::listbox disallow to remove items because of immutable model");
}
void assign(std::size_t pos, const std::vector<cell>& cells) override
{
throw std::runtime_error("nana::listbox disallow to remove items because of immutable model");
}
std::vector<cell> to_cells(std::size_t pos) const override
{
return ctrans_(container_.at(pos));
}
bool push_back(const_virtual_pointer& dptr) override
{
throw std::runtime_error("nana::listbox disallow to remove items because of immutable model");
}
void* pointer() override
{
return nullptr;
}
const void* pointer() const override
{
return &container_;
}
private:
container_reference container_;
cell_translator ctrans_;
};
class model_interface
{
public:
virtual ~model_interface() = default;
virtual void lock() = 0;
virtual void unlock() = 0;
virtual container_interface* container() = 0;
virtual const container_interface* container() const = 0;
};
class model_guard
{
model_guard(const model_guard&) = delete;
model_guard& operator=(const model_guard&) = delete;
public:
model_guard(model_interface* model)
: model_(model)
{
model->lock();
}
model_guard(model_guard&& other)
: model_(other.model_)
{
other.model_ = nullptr;
}
~model_guard() noexcept
{
if (model_)
model_->unlock();
}
model_guard& operator=(model_guard&& other)
{
if (this != &other)
{
if (model_)
model_->unlock();
model_ = other.model_;
other.model_ = nullptr;
}
return *this;
}
template<typename STLContainer>
STLContainer& container()
{
using stlcontainer = typename std::decay<STLContainer>::type;
if (!model_)
throw std::runtime_error("nana::listbox empty model_guard");
using type = basic_container<stlcontainer>;
auto p = dynamic_cast<type*>(model_->container());
if (nullptr == p)
throw std::invalid_argument("invalid listbox model container type");
if (nullptr == p->pointer())
throw std::runtime_error("the modal is immutable");
return *static_cast<stlcontainer*>(p->pointer());
}
template<typename STLContainer>
const STLContainer& container() const
{
using stlcontainer = typename std::decay<STLContainer>::type;
if (!model_)
throw std::runtime_error("nana::listbox empty model_guard");
using type = basic_container<stlcontainer>;
auto p = dynamic_cast<const type*>(model_->container());
if (nullptr == p)
throw std::invalid_argument("invalid listbox model container type");
return *static_cast<const stlcontainer*>(p->pointer());
}
private:
model_interface* model_;
};
template<typename STLContainer, typename Mutex>
class standalone_model_container
: public model_interface
{
public:
using value_translator = typename container_translator<typename STLContainer::value_type>::value_translator;
using cell_translator = typename container_translator<typename STLContainer::value_type>::cell_translator;
standalone_model_container(STLContainer&& container, value_translator vtrans, cell_translator ctrans)
: container_(std::move(container), std::move(vtrans), std::move(ctrans))
{
}
standalone_model_container(const STLContainer& container, value_translator vtrans, cell_translator ctrans)
: container_(container, std::move(vtrans), std::move(ctrans))
{
}
void lock() override
{
mutex_.lock();
}
void unlock() override
{
mutex_.unlock();
}
container_interface* container() override
{
return &container_;
}
const container_interface* container() const override
{
return &container_;
}
private:
Mutex mutex_;
standalone_container<STLContainer> container_;
};
template<typename STLContainer, typename Mutex>
class shared_model_container
: public model_interface
{
public:
using value_translator = typename container_translator<typename STLContainer::value_type>::value_translator;
using cell_translator = typename container_translator<typename STLContainer::value_type>::cell_translator;
shared_model_container(STLContainer& container, value_translator vtrans, cell_translator ctrans)
: container_ptr_(new shared_container<STLContainer>(container, std::move(vtrans), std::move(ctrans)))
{
}
shared_model_container(const STLContainer& container, cell_translator ctrans)
: container_ptr_(new shared_immutable_container<STLContainer>(container, std::move(ctrans)))
{
}
void lock() override
{
mutex_.lock();
}
void unlock() override
{
mutex_.unlock();
}
container_interface* container() override
{
return container_ptr_.get();
}
const container_interface* container() const override
{
return container_ptr_.get();
}
private:
Mutex mutex_;
std::unique_ptr<container_interface> container_ptr_;
};
using size_type = std::size_t; using size_type = std::size_t;
using native_string_type = ::nana::detail::native_string_type; using native_string_type = ::nana::detail::native_string_type;
@ -89,36 +628,15 @@ namespace nana
using inline_notifier_interface = detail::inline_widget_notifier_interface<index_pair, std::string>; using inline_notifier_interface = detail::inline_widget_notifier_interface<index_pair, std::string>;
struct cell // struct essence_t
{ //@brief: this struct gives many data for listbox,
struct format // the state of the struct does not effect on member funcions, therefore all data members are public.
{ struct essence_t;
::nana::color bgcolor;
::nana::color fgcolor;
/// ::nana::paint::font font; \todo
format() = default;
format(const ::nana::color& bgcolor, const ::nana::color& fgcolor);
};
using format_ptr = ::std::unique_ptr<format>;
::std::string text;
format_ptr custom_format;
cell() = default;
cell(const cell&);
cell(cell&&);
cell(::std::string);
cell(::std::string, const format&);
cell(::std::string, const ::nana::color& bgcolor, const ::nana::color& fgcolor);
cell& operator=(const cell&);
cell& operator=(cell&&);
};
class oresolver class oresolver
{ {
public: public:
oresolver(essence_t*);
oresolver& operator<<(bool); oresolver& operator<<(bool);
oresolver& operator<<(short); oresolver& operator<<(short);
oresolver& operator<<(unsigned short); oresolver& operator<<(unsigned short);
@ -141,7 +659,10 @@ namespace nana
oresolver& operator<<(std::nullptr_t); oresolver& operator<<(std::nullptr_t);
std::vector<cell> && move_cells(); std::vector<cell> && move_cells();
::nana::listbox& listbox();
private: private:
essence_t* const ess_;
std::vector<cell> cells_; std::vector<cell> cells_;
}; };
@ -174,11 +695,6 @@ namespace nana
using selection = std::vector<index_pair>; using selection = std::vector<index_pair>;
/// struct essence_t
///@brief: this struct gives many data for listbox,
/// the state of the struct does not effect on member funcions, therefore all data members are public.
struct essence_t;
struct category_t; struct category_t;
class drawer_header_impl; class drawer_header_impl;
class drawer_lister_impl; class drawer_lister_impl;
@ -256,7 +772,7 @@ namespace nana
template<typename T> template<typename T>
item_proxy & resolve_from(const T& t) item_proxy & resolve_from(const T& t)
{ {
oresolver ores; oresolver ores(_m_ess());
ores << t; ores << t;
auto && cells = ores.move_cells(); auto && cells = ores.move_cells();
auto cols = columns(); auto cols = columns();
@ -355,6 +871,8 @@ namespace nana
{ {
public: public:
using inline_notifier_interface = drawerbase::listbox::inline_notifier_interface; using inline_notifier_interface = drawerbase::listbox::inline_notifier_interface;
template<typename Value> using value_translator = typename container_translator<Value>::value_translator;
template<typename Value> using cell_translator = typename container_translator<Value>::cell_translator;
cat_proxy() = default; cat_proxy() = default;
cat_proxy(essence_t*, size_type pos); cat_proxy(essence_t*, size_type pos);
@ -364,7 +882,11 @@ namespace nana
template<typename T> template<typename T>
item_proxy append(T&& t, bool set_value = false) item_proxy append(T&& t, bool set_value = false)
{ {
oresolver ores; oresolver ores(ess_);
//Troubleshoot:
//If a compiler error that no operator<< overload found for type T occurs, please define a overload operator<<(oresolver&, const T&).
//If a listbox have a model set, try call append_model instead.
if (set_value) if (set_value)
ores << t; //copy it if it is rvalue and set_value is true. ores << t; //copy it if it is rvalue and set_value is true.
else else
@ -381,6 +903,33 @@ namespace nana
return iter; return iter;
} }
template<typename T>
void append_model(const T& t)
{
_m_try_append_model(const_virtual_pointer{ &t });
_m_update();
}
template<typename Mutex, typename STLContainer, typename ValueTranslator, typename CellTranslator>
void model(STLContainer&& container, ValueTranslator vtrans, CellTranslator ctrans)
{
_m_reset_model(new standalone_model_container<typename std::decay<STLContainer>::type, Mutex>(std::forward<STLContainer>(container), std::move(vtrans), std::move(ctrans)));
}
template<typename Mutex, typename STLContainer, typename ValueTranslator, typename CellTranslator>
void shared_model(STLContainer& container, ValueTranslator vtrans, CellTranslator ctrans)
{
_m_reset_model(new shared_model_container<typename std::decay<STLContainer>::type, Mutex>(container, std::move(vtrans), std::move(ctrans)));
}
template<typename Mutex, typename STLContainer, typename CellTranslator>
void shared_model(const STLContainer& container, CellTranslator ctrans)
{
_m_reset_model(new shared_model_container<typename std::decay<STLContainer>::type, Mutex>(container, std::move(ctrans)));
}
model_guard model();
/// Appends one item at the end of this category with the specifies text in the column fields /// Appends one item at the end of this category with the specifies text in the column fields
void append(std::initializer_list<std::string> texts_utf8); void append(std::initializer_list<std::string> texts_utf8);
void append(std::initializer_list<std::wstring> texts); void append(std::initializer_list<std::wstring> texts);
@ -448,8 +997,10 @@ namespace nana
void inline_factory(size_type column, pat::cloneable<pat::abstract_factory<inline_notifier_interface>> factory); void inline_factory(size_type column, pat::cloneable<pat::abstract_factory<inline_notifier_interface>> factory);
private: private:
void _m_append(std::vector<cell> && cells); void _m_append(std::vector<cell> && cells);
void _m_try_append_model(const_virtual_pointer&);
void _m_cat_by_pos(); void _m_cat_by_pos();
void _m_update(); void _m_update();
void _m_reset_model(model_interface*);
private: private:
essence_t* ess_{nullptr}; essence_t* ess_{nullptr};
category_t* cat_{nullptr}; category_t* cat_{nullptr};
@ -480,7 +1031,7 @@ namespace nana
}; };
/// The event argument type for listbox's category_dbl_click /// The event argument type for listbox's category_dbl_click
struct arg_category struct arg_listbox_category
: public event_arg : public event_arg
{ {
drawerbase::listbox::cat_proxy category; drawerbase::listbox::cat_proxy category;
@ -491,7 +1042,7 @@ namespace nana
/// Determines whether expension/shrink of category is blocked /// Determines whether expension/shrink of category is blocked
bool category_change_blocked() const noexcept; bool category_change_blocked() const noexcept;
arg_category(const drawerbase::listbox::cat_proxy&) noexcept; arg_listbox_category(const drawerbase::listbox::cat_proxy&) noexcept;
private: private:
mutable bool block_change_; mutable bool block_change_;
}; };
@ -507,7 +1058,7 @@ namespace nana
basic_event<arg_listbox> selected; basic_event<arg_listbox> selected;
/// An event occurs when a listbox category is double clicking. /// An event occurs when a listbox category is double clicking.
basic_event<arg_category> category_dbl_click; basic_event<arg_listbox_category> category_dbl_click;
}; };
struct scheme struct scheme
@ -600,7 +1151,6 @@ By \a clicking on one header the list get \a reordered, first up, and then down
unsigned header_width(size_type position) const; unsigned header_width(size_type position) const;
unsigned auto_width(size_type position, unsigned max=3000); unsigned auto_width(size_type position, unsigned max=3000);
cat_proxy append(std::string); ///< Appends a new category to the end cat_proxy append(std::string); ///< Appends a new category to the end
cat_proxy append(std::wstring); ///< Appends a new category to the end cat_proxy append(std::wstring); ///< Appends a new category to the end
void append(std::initializer_list<std::string>); ///< Appends categories to the end void append(std::initializer_list<std::string>); ///< Appends categories to the end
@ -707,7 +1257,7 @@ By \a clicking on one header the list get \a reordered, first up, and then down
private: private:
drawerbase::listbox::essence_t & _m_ess() const; drawerbase::listbox::essence_t & _m_ess() const;
nana::any* _m_anyobj(size_type cat, size_type index, bool allocate_if_empty) const; nana::any* _m_anyobj(size_type cat, size_type index, bool allocate_if_empty) const;
drawerbase::listbox::category_t* _m_at_key(std::shared_ptr<nana::detail::key_interface>); drawerbase::listbox::category_t* _m_at_key(std::shared_ptr<nana::detail::key_interface>&);
void _m_erase_key(nana::detail::key_interface*); void _m_erase_key(nana::detail::key_interface*);
}; };
}//end namespace nana }//end namespace nana

View File

@ -850,9 +850,6 @@ namespace detail
{ {
window_layer::paint(wd, false, refresh_tree); window_layer::paint(wd, false, refresh_tree);
this->map(wd, force_copy_to_screen); this->map(wd, force_copy_to_screen);
wd->drawer.graphics.save_as_file("d:\\button.bmp");
wd->root_graph->save_as_file("d:\\button_root.bmp");
} }
else if (effects::edge_nimbus::none != wd->effect.edge_nimbus) else if (effects::edge_nimbus::none != wd->effect.edge_nimbus)
{ {

File diff suppressed because it is too large Load Diff