add notify_status for listbox inline widget

This commit is contained in:
Jinhao 2016-09-17 03:56:23 +08:00
parent 07229e566d
commit d9fb3b3ff9
3 changed files with 114 additions and 50 deletions

View File

@ -1,7 +1,7 @@
/** /**
* A Inline Widget Interface Definition * A Inline Widget Interface Definition
* Nana C++ Library(http://www.nanapro.org) * Nana C++ Library(http://www.nanapro.org)
* Copyright(C) 2003-2015 Jinhao(cnjinhao@hotmail.com) * Copyright(C) 2003-2016 Jinhao(cnjinhao@hotmail.com)
* *
* Distributed under the Boost Software License, Version 1.0. * Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at * (See accompanying file LICENSE_1_0.txt or copy at
@ -35,6 +35,9 @@ namespace nana
/// Returns the host widget of the indicator /// Returns the host widget of the indicator
virtual ::nana::widget& host() const = 0; virtual ::nana::widget& host() const = 0;
/// Returns the position of column
virtual std::size_t column() const = 0;
/// Modifies the value of a item specified by pos /// Modifies the value of a item specified by pos
virtual void modify(index_type pos, const value_type&) const = 0; virtual void modify(index_type pos, const value_type&) const = 0;
@ -45,7 +48,7 @@ namespace nana
virtual void hovered(index_type) = 0; virtual void hovered(index_type) = 0;
}; };
template<typename Index, typename Value> template<typename Index, typename Status, typename Value>
class inline_widget_notifier_interface class inline_widget_notifier_interface
{ {
public: public:
@ -55,6 +58,9 @@ namespace nana
/// A type to the value of the item /// A type to the value of the item
using value_type = Value; using value_type = Value;
/// A type to the status
using status_type = Status;
/// A typedef name of a inline widget indicator /// A typedef name of a inline widget indicator
using inline_indicator = inline_widget_indicator<index_type, value_type>; using inline_indicator = inline_widget_indicator<index_type, value_type>;
@ -70,6 +76,9 @@ namespace nana
/// A message to activate the inline widget to attach a specified item /// A message to activate the inline widget to attach a specified item
virtual void activate(inline_indicator&, index_type) = 0; virtual void activate(inline_indicator&, index_type) = 0;
/// A message to change the status
virtual void notify_status(status_type, bool) = 0;
/// A message to resize the inline widget /// A message to resize the inline widget
virtual void resize(const size&) = 0; virtual void resize(const size&) = 0;

View File

@ -676,8 +676,14 @@ namespace nana
using index_pairs = ::std::vector<index_pair>; using index_pairs = ::std::vector<index_pair>;
using inline_notifier_interface = detail::inline_widget_notifier_interface<index_pair, ::std::string>; enum class inline_widget_status{
checked,
checking,
selected,
selecting
};
using inline_notifier_interface = detail::inline_widget_notifier_interface<index_pair, inline_widget_status, ::std::string>;
// struct essence // struct essence
//@brief: this struct gives many data for listbox, //@brief: this struct gives many data for listbox,

View File

@ -678,6 +678,16 @@ namespace nana
} }
}; };
struct inline_pane
{
::nana::panel<false> pane_bottom; //pane for pane_widget
::nana::panel<false> pane_widget; //pane for placing user-define widget
std::unique_ptr<inline_notifier_interface> inline_ptr;
inline_indicator * indicator;
index_pair item_pos; //The item index of the inline widget
std::size_t column_pos;
};
class es_lister class es_lister
{ {
public: public:
@ -726,6 +736,37 @@ namespace nana
std::string to_string(const export_options& exp_opt) const; std::string to_string(const export_options& exp_opt) const;
inline_pane * get_inline_pane(const index_pair& item_pos)
{
for (auto p : active_panes_)
{
if (p && (p->item_pos == item_pos))
return p;
}
return nullptr;
}
void emit_checked(index_pair pos)
{
item_proxy i(ess_, pos);
arg_listbox arg{ i };
wd_ptr()->events().checked.emit(arg, wd_ptr()->handle());
auto pane = get_inline_pane(pos);
if (pane)
pane->inline_ptr->notify_status(inline_widget_status::checking, i.checked());
}
void emit_selected(index_pair pos)
{
item_proxy i(ess_, pos);
arg_listbox arg{ i };
wd_ptr()->events().selected.emit(arg, wd_ptr()->handle());
auto pane = get_inline_pane(pos);
if (pane)
pane->inline_ptr->notify_status(inline_widget_status::selecting, i.selected());
}
// Definition is provided after struct essence // Definition is provided after struct essence
unsigned column_content_pixels(size_type pos) const; unsigned column_content_pixels(size_type pos) const;
@ -1121,6 +1162,14 @@ namespace nana
return get(pos.cat)->items.at(index); return get(pos.cat)->items.at(index);
} }
void append_active_panes(inline_pane* p)
{
if (nullptr == p)
active_panes_.clear();
else
active_panes_.push_back(p);
}
// Removes all items of a specified category // Removes all items of a specified category
// It throws when the category is out of range or has an immutable model. // It throws when the category is out of range or has an immutable model.
void clear(size_type cat) void clear(size_type cat)
@ -1463,9 +1512,7 @@ namespace nana
if(m.flags.checked != ck) if(m.flags.checked != ck)
{ {
m.flags.checked = ck; m.flags.checked = ck;
emit_checked(pos);
arg_listbox arg{ item_proxy{ess_, pos}};
wd_ptr()->events().checked.emit(arg, wd_ptr()->handle());
} }
++pos.item; ++pos.item;
} }
@ -1531,8 +1578,7 @@ namespace nana
changed = true; changed = true;
m.flags.selected = sel; m.flags.selected = sel;
arg_listbox arg{ item_proxy(ess_, i) }; this->emit_selected(i);
wd_ptr()->events().selected.emit(arg, wd_ptr()->handle());
if (m.flags.selected) if (m.flags.selected)
last_selected_abs = i; last_selected_abs = i;
@ -1622,18 +1668,17 @@ namespace nana
return (for_selection ? m.flags.selected : m.flags.checked); return (for_selection ? m.flags.selected : m.flags.checked);
}; };
auto do_cancel = [this, for_selection](category_t::container::value_type& m, std::size_t cat_pos, std::size_t item_pos) auto do_cancel = [this, for_selection](category_t::container::value_type& m, const index_pair& item_pos)
{ {
arg_listbox arg{ item_proxy(ess_, index_pair(cat_pos, item_pos)) };
if (for_selection) if (for_selection)
{ {
m.flags.selected = false; m.flags.selected = false;
widget_->events().selected.emit(arg, widget_->handle()); this->emit_selected(item_pos);
} }
else else
{ {
m.flags.checked = false; m.flags.checked = false;
widget_->events().checked.emit(arg, widget_->handle()); this->emit_checked(item_pos);
} }
}; };
@ -1645,7 +1690,7 @@ namespace nana
for (auto & m : i->items) for (auto & m : i->items)
{ {
if ((item_pos != except.item) && pred(m)) if ((item_pos != except.item) && pred(m))
do_cancel(m, except.cat, item_pos); do_cancel(m, index_pair{ except.cat, item_pos });
++item_pos; ++item_pos;
} }
@ -1661,7 +1706,7 @@ namespace nana
for (auto & m : cat.items) for (auto & m : cat.items)
{ {
if (pred(m)) if (pred(m))
do_cancel(m, cat_pos, item_pos); do_cancel(m, index_pair{ cat_pos, item_pos });
++item_pos; ++item_pos;
} }
} }
@ -1671,7 +1716,7 @@ namespace nana
for (auto & m : cat.items) for (auto & m : cat.items)
{ {
if ((item_pos != except.item) && pred(m)) if ((item_pos != except.item) && pred(m))
do_cancel(m, cat_pos, item_pos); do_cancel(m, index_pair{ cat_pos, item_pos });
++item_pos; ++item_pos;
} }
} }
@ -1705,18 +1750,17 @@ namespace nana
return (for_selection ? m.flags.selected : m.flags.checked); return (for_selection ? m.flags.selected : m.flags.checked);
}; };
auto cancel = [this, for_selection](category_t::container::value_type& m, std::size_t cat_pos, std::size_t item_pos) auto cancel = [this, for_selection](category_t::container::value_type& m, const index_pair& item_pos)
{ {
arg_listbox arg{ item_proxy(ess_, index_pair(cat_pos, item_pos)) };
if (for_selection) if (for_selection)
{ {
m.flags.selected = false; m.flags.selected = false;
widget_->events().selected.emit(arg, widget_->handle()); this->emit_selected(item_pos);
} }
else else
{ {
m.flags.checked = false; m.flags.checked = false;
widget_->events().checked.emit(arg, widget_->handle()); this->emit_checked(item_pos);
} }
}; };
@ -1732,7 +1776,7 @@ namespace nana
for (auto end = cat.items.end(); i != end; ++i) for (auto end = cat.items.end(); i != end; ++i)
{ {
if (pred(*i)) if (pred(*i))
cancel(*i, cat_pos, i - cat.items.begin()); cancel(*i, index_pair{ cat_pos, static_cast<size_type>(i - cat.items.begin()) });
} }
} }
++cat_pos; ++cat_pos;
@ -1755,7 +1799,7 @@ namespace nana
for (++i; i != end; ++i) for (++i; i != end; ++i)
{ {
if (pred(*i)) if (pred(*i))
cancel(*i, cat_pos, i - cat.items.begin()); cancel(*i, index_pair{ cat_pos, static_cast<std::size_t>(i - cat.items.begin()) });
} }
} }
} }
@ -1767,7 +1811,7 @@ namespace nana
for (auto & m : cat.items) for (auto & m : cat.items)
{ {
if (pred(m)) if (pred(m))
cancel(m, cat_pos, item_pos); cancel(m, index_pair{ cat_pos, item_pos });
++item_pos; ++item_pos;
} }
@ -1814,10 +1858,7 @@ namespace nana
if(m.flags.checked != ck) if(m.flags.checked != ck)
{ {
m.flags.checked = ck; m.flags.checked = ck;
this->emit_checked(index_pair{cat, index});
arg_listbox arg{ item_proxy(ess_, index_pair(cat, index)) };
wd_ptr()->events().checked.emit(arg, widget_->handle());
changed = true; changed = true;
} }
++index; ++index;
@ -2088,6 +2129,8 @@ namespace nana
bool single_selection_category_limited_{ false }; bool single_selection_category_limited_{ false };
bool single_check_{ false }; bool single_check_{ false };
bool single_check_category_limited_{ false }; bool single_check_category_limited_{ false };
std::vector<inline_pane*> active_panes_;
};//end class es_lister };//end class es_lister
@ -2097,6 +2140,8 @@ namespace nana
enum class item_state{normal, highlighted, pressed, grabbed, floated}; enum class item_state{normal, highlighted, pressed, grabbed, floated};
enum class parts{unknown = -1, header, lister, checker}; enum class parts{unknown = -1, header, lister, checker};
using inline_pane = inline_pane;
::nana::listbox* listbox_ptr{nullptr}; ::nana::listbox* listbox_ptr{nullptr};
::nana::listbox::scheme_type* scheme_ptr{nullptr}; ::nana::listbox::scheme_type* scheme_ptr{nullptr};
::nana::paint::graphics *graph{nullptr}; ::nana::paint::graphics *graph{nullptr};
@ -2131,16 +2176,6 @@ namespace nana
}scroll; }scroll;
struct inline_pane
{
::nana::panel<false> pane_bottom; //pane for pane_widget
::nana::panel<false> pane_widget; //pane for placing user-define widget
std::unique_ptr<inline_notifier_interface> inline_ptr;
inline_indicator * indicator;
index_pair item_pos; //The item index of the inline widget
std::size_t column_pos;
};
std::map<pat::detail::abstract_factory_base*, std::deque<std::unique_ptr<inline_pane>>> inline_table, inline_buffered_table; std::map<pat::detail::abstract_factory_base*, std::deque<std::unique_ptr<inline_pane>>> inline_table, inline_buffered_table;
essence() essence()
@ -3010,6 +3045,11 @@ namespace nana
return *ess_->lister.wd_ptr(); return *ess_->lister.wd_ptr();
} }
std::size_t column() const override
{
return column_pos_;
}
void modify(index_type pos, const value_type& value) const override void modify(index_type pos, const value_type& value) const override
{ {
ess_->lister.throw_if_immutable_model(pos); ess_->lister.throw_if_immutable_model(pos);
@ -3533,15 +3573,19 @@ namespace nana
public: public:
using item_state = essence::item_state; using item_state = essence::item_state;
using parts = essence::parts; using parts = essence::parts;
using status_type = inline_notifier_interface::status_type;
drawer_lister_impl(essence * es) drawer_lister_impl(essence * es)
:essence_(es) :essence_(es)
{} {}
void draw(const nana::rectangle& rect) const void draw(const nana::rectangle& rect)
{ {
internal_scope_guard lock; internal_scope_guard lock;
//clear active panes
essence_->lister.append_active_panes(nullptr);
//The count of items to be drawn //The count of items to be drawn
auto item_count = essence_->number_of_lister_items(true); auto item_count = essence_->number_of_lister_items(true);
if (0 == item_count) if (0 == item_count)
@ -3724,7 +3768,7 @@ namespace nana
nana::color bgcolor, nana::color bgcolor,
nana::color fgcolor, nana::color fgcolor,
item_state state item_state state
) const )
{ {
auto & item = cat.items[item_pos.item]; auto & item = cat.items[item_pos.item];
@ -3838,9 +3882,6 @@ namespace nana
else else
visible_state = false; visible_state = false;
::nana::size sz{ wdg_w, essence_->scheme_ptr->item_height };
inline_wdg->pane_widget.size(sz);
inline_wdg->inline_ptr->resize(sz);
draw_column = inline_wdg->inline_ptr->whether_to_draw(); draw_column = inline_wdg->inline_ptr->whether_to_draw();
@ -3848,6 +3889,13 @@ namespace nana
inline_wdg->column_pos = column_pos; inline_wdg->column_pos = column_pos;
inline_wdg->inline_ptr->activate(*inline_wdg->indicator, item_pos); inline_wdg->inline_ptr->activate(*inline_wdg->indicator, item_pos);
::nana::size sz{ wdg_w, essence_->scheme_ptr->item_height };
inline_wdg->pane_widget.size(sz);
inline_wdg->inline_ptr->resize(sz);
inline_wdg->inline_ptr->notify_status(status_type::selected, item.flags.selected);
inline_wdg->inline_ptr->notify_status(status_type::checked, item.flags.checked);
inline_wdg->indicator->attach(item_pos, inline_wdg); inline_wdg->indicator->attach(item_pos, inline_wdg);
//To reduce the memory usage, the cells may not be allocated //To reduce the memory usage, the cells may not be allocated
@ -3857,6 +3905,8 @@ namespace nana
inline_wdg->inline_ptr->set({}); inline_wdg->inline_ptr->set({});
API::show_window(inline_wdg->pane_bottom, visible_state); API::show_window(inline_wdg->pane_bottom, visible_state);
essence_->lister.append_active_panes(inline_wdg);
} }
} }
@ -4191,14 +4241,12 @@ namespace nana
{ {
item_ptr->flags.selected = sel; item_ptr->flags.selected = sel;
arg_listbox arg{ item_proxy{ essence_, abs_item_pos } }; lister.emit_selected(abs_item_pos);
lister.wd_ptr()->events().selected.emit(arg, lister.wd_ptr()->handle());
if (item_ptr->flags.selected) if (item_ptr->flags.selected)
{ {
lister.cancel_others_if_single_enabled(true, abs_item_pos); lister.cancel_others_if_single_enabled(true, abs_item_pos);
essence_->lister.last_selected_abs = abs_item_pos; essence_->lister.last_selected_abs = abs_item_pos;
} }
else if (essence_->lister.last_selected_abs == abs_item_pos) else if (essence_->lister.last_selected_abs == abs_item_pos)
essence_->lister.last_selected_abs.set_both(npos); essence_->lister.last_selected_abs.set_both(npos);
@ -4213,8 +4261,7 @@ namespace nana
{ {
item_ptr->flags.checked = ! item_ptr->flags.checked; item_ptr->flags.checked = ! item_ptr->flags.checked;
arg_listbox arg{ item_proxy{ essence_, abs_item_pos } }; lister.emit_checked(abs_item_pos);
lister.wd_ptr()->events().checked.emit(arg, lister.wd_ptr()->handle());
if (item_ptr->flags.checked) if (item_ptr->flags.checked)
lister.cancel_others_if_single_enabled(false, abs_item_pos); lister.cancel_others_if_single_enabled(false, abs_item_pos);
@ -4479,12 +4526,13 @@ namespace nana
item_proxy & item_proxy::check(bool ck) item_proxy & item_proxy::check(bool ck)
{ {
internal_scope_guard lock;
auto & m = cat_->items.at(pos_.item); auto & m = cat_->items.at(pos_.item);
if(m.flags.checked != ck) if(m.flags.checked != ck)
{ {
m.flags.checked = ck; m.flags.checked = ck;
arg_listbox arg{*this}; ess_->lister.emit_checked(pos_);
ess_->lister.wd_ptr()->events().checked.emit(arg, ess_->lister.wd_ptr()->handle());
ess_->update(); ess_->update();
} }
return *this; return *this;
@ -4498,13 +4546,14 @@ namespace nana
/// is ignored if no change (maybe set last_selected anyway??), but if change emit event, deselect others if need ans set/unset last_selected /// is ignored if no change (maybe set last_selected anyway??), but if change emit event, deselect others if need ans set/unset last_selected
item_proxy & item_proxy::select(bool s) item_proxy & item_proxy::select(bool s)
{ {
internal_scope_guard lock;
//pos_ never represents a category if this item_proxy is available. //pos_ never represents a category if this item_proxy is available.
auto & m = cat_->items.at(pos_.item); // a ref to the real item auto & m = cat_->items.at(pos_.item); // a ref to the real item
if(m.flags.selected == s) return *this; // ignore if no change if(m.flags.selected == s) return *this; // ignore if no change
m.flags.selected = s; // actually change selection m.flags.selected = s; // actually change selection
arg_listbox arg{*this}; ess_->lister.emit_selected(this->pos_);
ess_->lister.wd_ptr()->events().selected.emit(arg, ess_->lister.wd_ptr()->handle());
if (m.flags.selected) if (m.flags.selected)
{ {