add new scroll_into_view() for treebox

This commit is contained in:
Jinhao 2018-09-08 18:01:48 +08:00
parent 8d22ff3b51
commit c1b88c430a
2 changed files with 239 additions and 105 deletions

View File

@ -87,13 +87,15 @@ namespace nana
class trigger class trigger
:public drawer_trigger :public drawer_trigger
{ {
template<typename Renderer> //template<typename Renderer>
struct basic_implement; //struct basic_implement; //deprecated
class item_renderer; class implementation;
//class item_renderer; //deprecated
class item_locator; class item_locator;
typedef basic_implement<item_renderer> implement; //typedef basic_implement<item_renderer> implement; //deprecated
public: public:
struct treebox_node_type struct treebox_node_type
{ {
@ -116,20 +118,22 @@ namespace nana
trigger(); trigger();
~trigger(); ~trigger();
implement * impl() const; implementation * impl() const;
void check(node_type*, checkstate); void check(node_type*, checkstate);
void renderer(::nana::pat::cloneable<renderer_interface>&&); pat::cloneable<renderer_interface>& renderer() const;
const ::nana::pat::cloneable<renderer_interface>& renderer() const;
//void renderer(::nana::pat::cloneable<renderer_interface>&&);
//const ::nana::pat::cloneable<renderer_interface>& renderer() const; //deprecated
void placer(::nana::pat::cloneable<compset_placer_interface>&&); void placer(::nana::pat::cloneable<compset_placer_interface>&&);
const ::nana::pat::cloneable<compset_placer_interface>& placer() const; const ::nana::pat::cloneable<compset_placer_interface>& placer() const;
node_type* insert(node_type*, const std::string& key, std::string&&); node_type* insert(node_type*, const std::string& key, std::string&&);
node_type* insert(const std::string& path, std::string&&); node_type* insert(const std::string& path, std::string&&);
node_type * selected() const; //node_type * selected() const; //deprecated
void selected(node_type*); //void selected(node_type*);
node_image_tag& icon(const ::std::string&) const; node_image_tag& icon(const ::std::string&) const;
void icon_erase(const ::std::string&); void icon_erase(const ::std::string&);
@ -137,6 +141,7 @@ namespace nana
unsigned node_width(const node_type*) const; unsigned node_width(const node_type*) const;
bool rename(node_type*, const char* key, const char* name); bool rename(node_type*, const char* key, const char* name);
private: private:
//Overrides drawer_trigger methods //Overrides drawer_trigger methods
void attached(widget_reference, graph_reference) override; void attached(widget_reference, graph_reference) override;
@ -152,7 +157,7 @@ namespace nana
void key_press(graph_reference, const arg_keyboard&) override; void key_press(graph_reference, const arg_keyboard&) override;
void key_char(graph_reference, const arg_keyboard&) override; void key_char(graph_reference, const arg_keyboard&) override;
private: private:
implement * const impl_; implementation * const impl_;
}; //end class trigger }; //end class trigger
@ -378,7 +383,7 @@ namespace nana
template<typename ItemRenderer> template<typename ItemRenderer>
treebox & renderer(const ItemRenderer & rd) ///< set user-defined node renderer treebox & renderer(const ItemRenderer & rd) ///< set user-defined node renderer
{ {
get_drawer_trigger().renderer(::nana::pat::cloneable<renderer_interface>(rd)); get_drawer_trigger().renderer() = ::nana::pat::cloneable<renderer_interface>{rd};
return *this; return *this;
} }
@ -445,6 +450,9 @@ namespace nana
item_proxy selected() const; ///< returns the selected node item_proxy selected() const; ///< returns the selected node
/// Scrolls a specified item into view
void scroll_into_view(item_proxy item, align_v);
private: private:
std::shared_ptr<scroll_operation_interface> _m_scroll_operation() override; std::shared_ptr<scroll_operation_interface> _m_scroll_operation() override;

View File

@ -180,13 +180,13 @@ namespace nana
} }
};//end class tooltip_window };//end class tooltip_window
//item_locator should be defined before the definition of basic_implement //item_locator should be defined before the definition of implementation
class trigger::item_locator class trigger::item_locator
{ {
public: public:
using node_type = tree_cont_type::node_type; using node_type = tree_cont_type::node_type;
item_locator(implement * impl, int item_pos, int x, int y); item_locator(implementation * impl, int item_pos, int x, int y);
int operator()(node_type &node, int affect); int operator()(node_type &node, int affect);
node_type * node() const; node_type * node() const;
component what() const; component what() const;
@ -194,7 +194,7 @@ namespace nana
nana::rectangle text_pos() const; nana::rectangle text_pos() const;
private: private:
trigger::implement * impl_; implementation * const impl_;
nana::point item_pos_; nana::point item_pos_;
const nana::point pos_; //Mouse pointer position const nana::point pos_; //Mouse pointer position
component what_; component what_;
@ -212,11 +212,89 @@ namespace nana
} }
}; };
//struct implement //struct implementation
//@brief: some data for treebox trigger //@brief: some data for treebox trigger
template<typename Renderer> class trigger::implementation
struct trigger::basic_implement
{ {
class item_rendering_director
: public compset_interface
{
public:
using node_type = tree_cont_type::node_type;
item_rendering_director(implementation * impl, const nana::point& pos):
impl_(impl),
pos_(pos)
{
}
//affect
//0 = Sibling, the last is a sibling of node
//1 = Owner, the last is the owner of node
//>=2 = Children, the last is a child of a node that before this node.
int operator()(const node_type& node, int affect)
{
iterated_node_ = &node;
switch (affect)
{
case 1:
pos_.x += impl_->shape.indent_pixels;
break;
default:
if (affect >= 2)
pos_.x -= impl_->shape.indent_pixels * (affect - 1);
}
auto & comp_placer = impl_->data.comp_placer;
impl_->assign_node_attr(node_attr_, iterated_node_);
node_r_.x = node_r_.y = 0;
node_r_.width = comp_placer->item_width(*impl_->data.graph, node_attr_);
node_r_.height = comp_placer->item_height(*impl_->data.graph);
auto renderer = impl_->data.renderer;
renderer->begin_paint(*impl_->data.widget_ptr);
renderer->bground(*impl_->data.graph, this);
renderer->expander(*impl_->data.graph, this);
renderer->crook(*impl_->data.graph, this);
renderer->icon(*impl_->data.graph, this);
renderer->text(*impl_->data.graph, this);
pos_.y += node_r_.height;
if (pos_.y > static_cast<int>(impl_->data.graph->height()))
return 0;
return (node.child && node.value.second.expanded ? 1 : 2);
}
private:
//Overrides compset_interface
virtual const item_attribute_t& item_attribute() const override
{
return node_attr_;
}
virtual bool comp_attribute(component_t comp, comp_attribute_t& attr) const override
{
attr.area = node_r_;
if (impl_->data.comp_placer->locate(comp, node_attr_, &attr.area))
{
attr.mouse_pointed = node_attr_.mouse_pointed;
attr.area.x += pos_.x;
attr.area.y += pos_.y;
return true;
}
return false;
}
private:
implementation * const impl_;
::nana::point pos_;
const node_type * iterated_node_;
item_attribute_t node_attr_;
::nana::rectangle node_r_;
};
public:
using node_type = trigger::node_type; using node_type = trigger::node_type;
struct rep_tag struct rep_tag
@ -271,7 +349,7 @@ namespace nana
nana::timer timer; nana::timer timer;
}adjust; }adjust;
public: public:
basic_implement() implementation()
{ {
data.graph = nullptr; data.graph = nullptr;
data.widget_ptr = nullptr; data.widget_ptr = nullptr;
@ -353,6 +431,11 @@ namespace nana
return true; return true;
} }
static constexpr unsigned margin_top_bottom()
{
return 1;
}
bool draw(bool reset_scroll, bool ignore_update = false, bool ignore_auto_draw = false) bool draw(bool reset_scroll, bool ignore_update = false, bool ignore_auto_draw = false)
{ {
if(data.graph && (false == data.stop_drawing)) if(data.graph && (false == data.stop_drawing))
@ -368,7 +451,7 @@ namespace nana
data.graph->rectangle(true, data.widget_ptr->bgcolor()); data.graph->rectangle(true, data.widget_ptr->bgcolor());
//Draw tree //Draw tree
attr.tree_cont.for_each(shape.first, Renderer(this, nana::point(static_cast<int>(attr.tree_cont.indent_size(shape.first) * shape.indent_pixels) - shape.offset_x, 1))); attr.tree_cont.for_each(shape.first, item_rendering_director(this, nana::point(static_cast<int>(attr.tree_cont.indent_size(shape.first) * shape.indent_pixels) - shape.offset_x, margin_top_bottom())));
if (!ignore_update) if (!ignore_update)
API::update_window(data.widget_ptr->handle()); API::update_window(data.widget_ptr->handle());
@ -460,6 +543,41 @@ namespace nana
return nullptr; return nullptr;
} }
node_type* last(bool ignore_folded_children) const
{
auto p = attr.tree_cont.get_root();
while (true)
{
while (p->next)
p = p->next;
if (p->child)
{
if (p->value.second.expanded || !ignore_folded_children)
{
p = p->child;
continue;
}
}
break;
}
return p;
}
std::size_t screen_capacity(bool completed) const
{
auto const item_px = data.comp_placer->item_height(*data.graph);
auto screen_px = data.graph->size().height - (margin_top_bottom() << 1);
if (completed || ((screen_px % item_px) == 0))
return screen_px / item_px;
return screen_px / item_px + 1;
}
bool make_adjust(node_type * node, int reason) bool make_adjust(node_type * node, int reason)
{ {
if(!node) return false; if(!node) return false;
@ -1356,7 +1474,7 @@ namespace nana
//class trigger::item_locator //class trigger::item_locator
trigger::item_locator::item_locator(implement * impl, int item_pos, int x, int y) trigger::item_locator::item_locator(implementation * impl, int item_pos, int x, int y)
: impl_(impl), : impl_(impl),
item_pos_(item_pos, 1), item_pos_(item_pos, 1),
pos_(x, y), pos_(x, y),
@ -1439,86 +1557,6 @@ namespace nana
return{node_text_r_.x + item_pos_.x, node_text_r_.y + item_pos_.y, node_text_r_.width, node_text_r_.height}; return{node_text_r_.x + item_pos_.x, node_text_r_.y + item_pos_.y, node_text_r_.width, node_text_r_.height};
} }
//end class item_locator //end class item_locator
class trigger::item_renderer
: public compset_interface
{
public:
typedef tree_cont_type::node_type node_type;
item_renderer(implement * impl, const nana::point& pos)
: impl_(impl),
pos_(pos)
{
}
//affect
//0 = Sibling, the last is a sibling of node
//1 = Owner, the last is the owner of node
//>=2 = Children, the last is a child of a node that before this node.
int operator()(const node_type& node, int affect)
{
implement * draw_impl = impl_;
iterated_node_ = &node;
switch(affect)
{
case 1:
pos_.x += draw_impl->shape.indent_pixels;
break;
default:
if(affect >= 2)
pos_.x -= draw_impl->shape.indent_pixels * (affect - 1);
}
auto & comp_placer = impl_->data.comp_placer;
impl_->assign_node_attr(node_attr_, iterated_node_);
node_r_.x = node_r_.y = 0;
node_r_.width = comp_placer->item_width(*impl_->data.graph, node_attr_);
node_r_.height = comp_placer->item_height(*impl_->data.graph);
auto renderer = draw_impl->data.renderer;
renderer->begin_paint(*draw_impl->data.widget_ptr);
renderer->bground(*draw_impl->data.graph, this);
renderer->expander(*draw_impl->data.graph, this);
renderer->crook(*draw_impl->data.graph, this);
renderer->icon(*draw_impl->data.graph, this);
renderer->text(*draw_impl->data.graph, this);
pos_.y += node_r_.height;
if(pos_.y > static_cast<int>(draw_impl->data.graph->height()))
return 0;
return (node.child && node.value.second.expanded ? 1 : 2);
}
private:
//Overrides compset_interface
virtual const item_attribute_t& item_attribute() const override
{
return node_attr_;
}
virtual bool comp_attribute(component_t comp, comp_attribute_t& attr) const override
{
attr.area = node_r_;
if (impl_->data.comp_placer->locate(comp, node_attr_, &attr.area))
{
attr.mouse_pointed = node_attr_.mouse_pointed;
attr.area.x += pos_.x;
attr.area.y += pos_.y;
return true;
}
return false;
}
private:
trigger::implement * impl_;
::nana::point pos_;
const node_type * iterated_node_;
item_attribute_t node_attr_;
::nana::rectangle node_r_;
};
} }
//Treebox Implementation //Treebox Implementation
@ -1548,7 +1586,7 @@ namespace nana
//end struct treebox_node_type //end struct treebox_node_type
trigger::trigger() trigger::trigger()
: impl_(new implement) : impl_(new implementation)
{ {
impl_->data.trigger_ptr = this; impl_->data.trigger_ptr = this;
impl_->data.renderer = nana::pat::cloneable<renderer_interface>(internal_renderer()); impl_->data.renderer = nana::pat::cloneable<renderer_interface>(internal_renderer());
@ -1615,7 +1653,7 @@ namespace nana
delete impl_; delete impl_;
} }
trigger::implement * trigger::impl() const trigger::implementation * trigger::impl() const
{ {
return impl_; return impl_;
} }
@ -1687,6 +1725,7 @@ namespace nana
} }
} }
/* //deprecated
void trigger::renderer(::nana::pat::cloneable<renderer_interface>&& r) void trigger::renderer(::nana::pat::cloneable<renderer_interface>&& r)
{ {
impl_->data.renderer = std::move(r); impl_->data.renderer = std::move(r);
@ -1696,6 +1735,12 @@ namespace nana
{ {
return impl_->data.renderer; return impl_->data.renderer;
} }
*/
::nana::pat::cloneable<renderer_interface>& trigger::renderer() const
{
return impl_->data.renderer;
}
void trigger::placer(::nana::pat::cloneable<compset_placer_interface>&& r) void trigger::placer(::nana::pat::cloneable<compset_placer_interface>&& r)
{ {
@ -1729,16 +1774,20 @@ namespace nana
return x; return x;
} }
trigger::node_type* trigger::selected() const /*
trigger::node_type* trigger::selected() const //deprecated
{ {
return impl_->node_state.selected; return impl_->node_state.selected;
} }
*/
void trigger::selected(node_type* node) /*
void trigger::selected(node_type* node) //deprecated
{ {
if(impl_->attr.tree_cont.verify(node) && impl_->set_selected(node)) if(impl_->attr.tree_cont.verify(node) && impl_->set_selected(node))
impl_->draw(true); impl_->draw(true);
} }
*/
node_image_tag& trigger::icon(const std::string& id) const node_image_tag& trigger::icon(const std::string& id) const
{ {
@ -2233,7 +2282,84 @@ namespace nana
treebox::item_proxy treebox::selected() const treebox::item_proxy treebox::selected() const
{ {
return item_proxy(const_cast<drawer_trigger_t*>(&get_drawer_trigger()), get_drawer_trigger().selected()); //return item_proxy(const_cast<drawer_trigger_t*>(&get_drawer_trigger()), get_drawer_trigger().selected()); //deprecated
auto dw = &get_drawer_trigger();
return item_proxy(const_cast<drawer_trigger_t*>(dw), dw->impl()->node_state.selected);
}
void treebox::scroll_into_view(item_proxy item, align_v align)
{
auto node = item._m_node();
internal_scope_guard lock;
auto impl = get_drawer_trigger().impl();
auto & tree = impl->attr.tree_cont;
auto parent = node->owner;
std::vector<drawerbase::treebox::node_type*> parent_path;
while (parent)
{
parent_path.push_back(parent);
parent = parent->owner;
}
//Expands the shrinked nodes which are ancestors of node
for (auto i = parent_path.rbegin(); i != parent_path.rend(); ++i)
{
if (!(*i)->value.second.expanded)
{
(*i)->value.second.expanded = true;
item_proxy iprx(impl->data.trigger_ptr, *i);
impl->data.widget_ptr->events().expanded.emit(::nana::arg_treebox{ *impl->data.widget_ptr, iprx, true }, impl->data.widget_ptr->handle());
}
}
auto pos = tree.distance_if(node, drawerbase::treebox::pred_allow_child{});
auto last_pos = tree.distance_if(impl->last(true), drawerbase::treebox::pred_allow_child{});
auto const capacity = impl->screen_capacity(false);
auto const item_px = impl->data.comp_placer->item_height(*impl->data.graph);
if (align_v::top == align)
{
if (last_pos - pos + 1 < capacity)
{
if (last_pos + 1 >= capacity)
pos = last_pos + 1 - capacity;
else
pos = 0;
}
}
else if (align_v::center == align)
{
auto const short_side = (std::min)(pos, last_pos - pos);
if (short_side >= capacity / 2)
{
pos -= short_side;
}
else
{
if (short_side == pos || (last_pos + 1 < capacity))
pos = 0;
else
pos = last_pos + 1 - capacity;
}
}
else if (align_v::bottom == align)
{
if (pos + 1 >= capacity)
pos = pos + 1 - capacity;
else
pos = 0;
}
impl->shape.first = impl->attr.tree_cont.advance_if(nullptr, pos, drawerbase::treebox::pred_allow_child{});
impl->draw(true, false, true);
API::update_window(*this);
} }
std::shared_ptr<scroll_operation_interface> treebox::_m_scroll_operation() std::shared_ptr<scroll_operation_interface> treebox::_m_scroll_operation()