945 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			945 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/*
 | 
						|
 *	A Categorize Implementation
 | 
						|
 *	Nana C++ Library(http://www.nanapro.org)
 | 
						|
 *	Copyright(C) 2003-2015 Jinhao(cnjinhao@hotmail.com)
 | 
						|
 *
 | 
						|
 *	Distributed under the Boost Software License, Version 1.0.
 | 
						|
 *	(See accompanying file LICENSE_1_0.txt or copy at
 | 
						|
 *	http://www.boost.org/LICENSE_1_0.txt)
 | 
						|
 *
 | 
						|
 *	@file: nana/gui/widgets/categorize.cpp
 | 
						|
 */
 | 
						|
 | 
						|
#include <nana/gui/wvl.hpp>
 | 
						|
#include <nana/gui/widgets/categorize.hpp>
 | 
						|
#include <nana/gui/widgets/float_listbox.hpp>
 | 
						|
#include <nana/gui/element.hpp>
 | 
						|
#include <nana/paint/gadget.hpp>
 | 
						|
#include <nana/gui/widgets/detail/tree_cont.hpp>
 | 
						|
#include <stdexcept>
 | 
						|
 | 
						|
namespace nana
 | 
						|
{
 | 
						|
	namespace drawerbase
 | 
						|
	{
 | 
						|
		namespace categorize
 | 
						|
		{
 | 
						|
			struct event_agent_holder
 | 
						|
			{
 | 
						|
				std::function<void(nana::any&)> selected;
 | 
						|
			};
 | 
						|
 | 
						|
			struct item
 | 
						|
				: public float_listbox::item_interface
 | 
						|
			{
 | 
						|
				nana::paint::image	item_image;
 | 
						|
				nana::string		item_text;
 | 
						|
			public:
 | 
						|
				item(const nana::string& s)
 | 
						|
					: item_text(s)
 | 
						|
				{}
 | 
						|
			public:
 | 
						|
				//Implement item_interface methods
 | 
						|
				const nana::paint::image& image() const override
 | 
						|
				{
 | 
						|
					return item_image;
 | 
						|
				}
 | 
						|
 | 
						|
				const nana::char_t * text() const override
 | 
						|
				{
 | 
						|
					return item_text.data();
 | 
						|
				}
 | 
						|
			};
 | 
						|
 | 
						|
			struct item_tag
 | 
						|
			{
 | 
						|
				nana::size	scale;
 | 
						|
				unsigned	pixels;
 | 
						|
				nana::any	value;
 | 
						|
			};
 | 
						|
 | 
						|
			//class renderer
 | 
						|
					renderer::ui_element::ui_element()
 | 
						|
						: what(none), index(0)
 | 
						|
					{}
 | 
						|
				renderer::~renderer(){}
 | 
						|
			//end class renderer
 | 
						|
 | 
						|
			//interior_renderer
 | 
						|
 | 
						|
			class interior_renderer
 | 
						|
				: public renderer
 | 
						|
			{
 | 
						|
			private:
 | 
						|
				void background(graph_reference graph, window wd, const nana::rectangle& r, const ui_element& ue)
 | 
						|
				{
 | 
						|
					ui_el_ = ue;
 | 
						|
					style_.bgcolor = API::bgcolor(wd);
 | 
						|
					style_.fgcolor = API::fgcolor(wd);
 | 
						|
 | 
						|
					if(ue.what == ue.none || (API::window_enabled(wd) == false))
 | 
						|
					{	//the mouse is out of the widget.
 | 
						|
						style_.bgcolor = style_.bgcolor.blend(color{ 0xa0, 0xc9, 0xf5 }, 0.9);
 | 
						|
					}
 | 
						|
					graph.rectangle(r, true, style_.bgcolor);
 | 
						|
				}
 | 
						|
 | 
						|
				virtual void root_arrow(graph_reference graph, const nana::rectangle& r, mouse_action state)
 | 
						|
				{
 | 
						|
					::nana::rectangle arrow_r{r.x + static_cast<int>(r.width - 16) / 2, r.y + static_cast<int>(r.height - 16) / 2, 16, 16};
 | 
						|
 | 
						|
					if(ui_el_.what == ui_el_.item_root)
 | 
						|
					{
 | 
						|
						_m_item_bground(graph, r.x + 1, r.y, r.width - 2, r.height, (state == mouse_action::pressed ? mouse_action::pressed : mouse_action::over));
 | 
						|
						graph.rectangle(r, false, color{ 0x3C, 0x7F, 0xB1 });
 | 
						|
						if(state == mouse_action::pressed)
 | 
						|
						{
 | 
						|
							++arrow_r.x;
 | 
						|
							++arrow_r.y;
 | 
						|
						}
 | 
						|
					}
 | 
						|
					else
 | 
						|
						graph.rectangle(r, true, style_.bgcolor);
 | 
						|
 | 
						|
					facade<element::arrow> arrow("double");
 | 
						|
					arrow.direction(::nana::direction::west);
 | 
						|
					arrow.draw(graph, {}, style_.fgcolor, arrow_r, element_state::normal);
 | 
						|
				}
 | 
						|
 | 
						|
				void item(graph_reference graph, const nana::rectangle& r, std::size_t index, const nana::string& name, unsigned txtheight, bool has_child, mouse_action state)
 | 
						|
				{
 | 
						|
					nana::point strpos(r.x + 5, r.y + static_cast<int>(r.height - txtheight) / 2);
 | 
						|
 | 
						|
					if((ui_el_.what == ui_el_.item_arrow || ui_el_.what == ui_el_.item_name) && (ui_el_.index == index))
 | 
						|
					{
 | 
						|
						mouse_action state_arrow, state_name;
 | 
						|
						if(mouse_action::pressed != state)
 | 
						|
						{
 | 
						|
							state_arrow = (ui_el_.what == ui_el_.item_arrow ? mouse_action::over : mouse_action::normal);
 | 
						|
							state_name = (ui_el_.what == ui_el_.item_name ? mouse_action::over : mouse_action::normal);
 | 
						|
						}
 | 
						|
						else
 | 
						|
						{
 | 
						|
							state_name = state_arrow = mouse_action::pressed;
 | 
						|
							++strpos.x;
 | 
						|
							++strpos.y;
 | 
						|
						}
 | 
						|
 | 
						|
						int top = r.y + 1;
 | 
						|
						unsigned width = r.width - 2;
 | 
						|
						unsigned height = r.height - 2;
 | 
						|
 | 
						|
						::nana::color clr{ 0x3C, 0x7F, 0xB1 };
 | 
						|
						if(has_child)
 | 
						|
						{
 | 
						|
							width -= 16;
 | 
						|
 | 
						|
							int left = r.x + r.width - 17;
 | 
						|
							_m_item_bground(graph, left + 1, top, 15, height, state_arrow);
 | 
						|
							graph.line({ left, top }, { left, r.y + static_cast<int>(height) }, clr);
 | 
						|
						}
 | 
						|
 | 
						|
						_m_item_bground(graph, r.x + 1, top, width, height, state_name);
 | 
						|
						graph.rectangle(r, false, clr);
 | 
						|
					}
 | 
						|
					graph.string(strpos, name, style_.fgcolor);
 | 
						|
 | 
						|
					if(has_child)
 | 
						|
					{
 | 
						|
						facade<element::arrow> arrow("double");
 | 
						|
						arrow.direction(::nana::direction::east);
 | 
						|
						arrow.draw(graph, {}, style_.fgcolor, { r.right() - 16, r.y + static_cast<int>(r.height - 16) / 2, 16, 16 }, element_state::normal);
 | 
						|
					}
 | 
						|
				}
 | 
						|
 | 
						|
				void border(graph_reference graph)
 | 
						|
				{
 | 
						|
					rectangle r{ graph.size() };
 | 
						|
 | 
						|
					graph.rectangle(r, false, static_cast<color_rgb>(0xf0f0f0));
 | 
						|
 | 
						|
					color lb(static_cast<color_rgb>(0x9dabb9));
 | 
						|
					color tr(static_cast<color_rgb>(0x484e55));
 | 
						|
					graph.frame_rectangle(r.pare_off(1), lb, tr, tr, lb);
 | 
						|
				}
 | 
						|
			private:
 | 
						|
				void _m_item_bground(graph_reference graph, int x, int y, unsigned width, unsigned height, mouse_action state)
 | 
						|
				{
 | 
						|
					const unsigned half = (height - 2) / 2;
 | 
						|
					int left = x + 1;
 | 
						|
					int top = y + 1;
 | 
						|
					nana::color clr_top(static_cast<color_rgb>(0xEAEAEA)), clr_bottom(static_cast<color_rgb>(0xDCDCDC));
 | 
						|
					switch(state)
 | 
						|
					{
 | 
						|
					case mouse_action::over:
 | 
						|
						clr_top.from_rgb(0xdf, 0xf2, 0xfc);
 | 
						|
						clr_bottom.from_rgb(0xa9, 0xda, 0xf5);
 | 
						|
						break;
 | 
						|
					case mouse_action::pressed:
 | 
						|
						clr_top.from_rgb(0xa6, 0xd7, 0xf2);
 | 
						|
						clr_bottom.from_rgb(0x92, 0xc4, 0xf6);
 | 
						|
						++left;
 | 
						|
						++top;
 | 
						|
						break;
 | 
						|
					default:
 | 
						|
						break;
 | 
						|
					}
 | 
						|
 | 
						|
					graph.rectangle(rectangle{ left, top, width - 2, half }, true, clr_top);
 | 
						|
					graph.rectangle(rectangle{ left, top + static_cast<int>(half), width - 2, (height - 2) - half }, true, clr_bottom);
 | 
						|
					if(mouse_action::pressed == state)
 | 
						|
					{
 | 
						|
						int bottom = y + height - 1;
 | 
						|
						int right = x + width - 1;
 | 
						|
 | 
						|
						graph.set_color(static_cast<color_rgb>(0x6E8D9F));
 | 
						|
						graph.line(point{ x, y }, point{right, y});
 | 
						|
						graph.line(point{ x, y + 1 }, point{ x, bottom });
 | 
						|
						++x;
 | 
						|
						++y;
 | 
						|
						graph.set_color(color(0xa6, 0xc7, 0xd9));
 | 
						|
						graph.line(point{ x, y }, point{ right, y });
 | 
						|
						graph.line(point{ x, y + 1 }, point{ x, bottom });
 | 
						|
					}
 | 
						|
				}
 | 
						|
 | 
						|
			private:
 | 
						|
				ui_element	ui_el_;
 | 
						|
				struct style_tag
 | 
						|
				{
 | 
						|
					color bgcolor;
 | 
						|
					color fgcolor;
 | 
						|
				}style_;
 | 
						|
			};
 | 
						|
 | 
						|
			class tree_wrapper
 | 
						|
			{
 | 
						|
			public:
 | 
						|
				using container = widgets::detail::tree_cont<item_tag>;
 | 
						|
				using node_handle = container::node_type*;
 | 
						|
 | 
						|
				tree_wrapper()
 | 
						|
					:splitstr_(STR("\\")), cur_(nullptr)
 | 
						|
				{}
 | 
						|
 | 
						|
				bool seq(std::size_t index, std::vector<node_handle> & seqv) const
 | 
						|
				{
 | 
						|
					_m_read_node_path(seqv);
 | 
						|
 | 
						|
					if(index < seqv.size())
 | 
						|
					{
 | 
						|
						if(index)
 | 
						|
							seqv.erase(seqv.begin(), seqv.begin() + index);
 | 
						|
						return true;
 | 
						|
					}
 | 
						|
					return false;
 | 
						|
				}
 | 
						|
 | 
						|
				void splitstr(const nana::string& ss)
 | 
						|
				{
 | 
						|
					if(ss.size())
 | 
						|
						splitstr_ = ss;
 | 
						|
				}
 | 
						|
 | 
						|
				const nana::string& splitstr() const
 | 
						|
				{
 | 
						|
					return splitstr_;
 | 
						|
				}
 | 
						|
 | 
						|
				nana::string path() const
 | 
						|
				{
 | 
						|
					std::vector<node_handle> v;
 | 
						|
					_m_read_node_path(v);
 | 
						|
 | 
						|
					nana::string str;
 | 
						|
					bool not_head = false;
 | 
						|
					for(auto i : v)
 | 
						|
					{
 | 
						|
						if(not_head)
 | 
						|
							str += splitstr_;
 | 
						|
						else
 | 
						|
							not_head = true;
 | 
						|
						str += i->value.first;
 | 
						|
					}
 | 
						|
					return str;
 | 
						|
				}
 | 
						|
 | 
						|
				void path(const nana::string& key)
 | 
						|
				{
 | 
						|
					cur_ = tree_.ref(key);
 | 
						|
				}
 | 
						|
 | 
						|
				node_handle at(std::size_t index) const
 | 
						|
				{
 | 
						|
					std::vector<node_handle> v;
 | 
						|
					_m_read_node_path(v);
 | 
						|
					return (index < v.size() ? v[index] : nullptr);
 | 
						|
				}
 | 
						|
 | 
						|
				node_handle tail(std::size_t index)
 | 
						|
				{
 | 
						|
					node_handle i = at(index);
 | 
						|
					if(i)	cur_ = i;
 | 
						|
					return i;
 | 
						|
				}
 | 
						|
 | 
						|
				node_handle cur() const
 | 
						|
				{
 | 
						|
					return cur_;
 | 
						|
				}
 | 
						|
 | 
						|
				void cur(node_handle i)
 | 
						|
				{
 | 
						|
					cur_ = i;
 | 
						|
				}
 | 
						|
 | 
						|
				void insert(const nana::string& name, const nana::any& value)
 | 
						|
				{
 | 
						|
					item_tag m;
 | 
						|
					m.pixels = 0;
 | 
						|
					m.value = value;
 | 
						|
					cur_ = tree_.insert(cur_, name, m);
 | 
						|
				}
 | 
						|
 | 
						|
				bool childset(const nana::string& name, const nana::any& value)
 | 
						|
				{
 | 
						|
					if(cur_)
 | 
						|
					{
 | 
						|
						item_tag m;
 | 
						|
						m.pixels = 0;
 | 
						|
						m.value = value;
 | 
						|
						tree_.insert(cur_, name, m);
 | 
						|
						return true;
 | 
						|
					}
 | 
						|
					return false;
 | 
						|
				}
 | 
						|
 | 
						|
				bool childset_erase(const nana::string& name)
 | 
						|
				{
 | 
						|
					if(cur_)
 | 
						|
					{
 | 
						|
						for(node_handle i = cur_->child; i; i = i->next)
 | 
						|
						{
 | 
						|
							if(i->value.first == name)
 | 
						|
							{
 | 
						|
								tree_.remove(i);
 | 
						|
								return true;
 | 
						|
							}
 | 
						|
						}
 | 
						|
					}
 | 
						|
					return false;
 | 
						|
				}
 | 
						|
 | 
						|
				node_handle find_child(const nana::string& name) const
 | 
						|
				{
 | 
						|
					if(cur_)
 | 
						|
					{
 | 
						|
						for(node_handle i = cur_->child; i; i = i->next)
 | 
						|
						{
 | 
						|
							if(i->value.first == name)
 | 
						|
								return i;
 | 
						|
						}
 | 
						|
					}
 | 
						|
					return nullptr;
 | 
						|
				}
 | 
						|
 | 
						|
				bool clear()
 | 
						|
				{
 | 
						|
					if(tree_.get_root()->child)
 | 
						|
					{
 | 
						|
						tree_.clear();
 | 
						|
						return true;
 | 
						|
					}
 | 
						|
					return false;
 | 
						|
				}
 | 
						|
			private:
 | 
						|
				void _m_read_node_path(std::vector<node_handle>& v) const
 | 
						|
				{
 | 
						|
					node_handle root = tree_.get_root();
 | 
						|
					for(node_handle i = cur_; i && (i != root); i = i->owner)
 | 
						|
						v.insert(v.begin(), i);
 | 
						|
				}
 | 
						|
			private:
 | 
						|
				container tree_;
 | 
						|
				nana::string splitstr_;
 | 
						|
				node_handle cur_;
 | 
						|
			};
 | 
						|
 | 
						|
			//class scheme
 | 
						|
			class trigger::scheme
 | 
						|
			{
 | 
						|
			public:
 | 
						|
				typedef tree_wrapper container;
 | 
						|
				typedef container::node_handle node_handle;
 | 
						|
				typedef renderer::ui_element	ui_element;
 | 
						|
 | 
						|
				enum class mode
 | 
						|
				{
 | 
						|
					normal, floatlist
 | 
						|
				};
 | 
						|
 | 
						|
				scheme()
 | 
						|
				{
 | 
						|
					proto_.ui_renderer = pat::cloneable<renderer>(interior_renderer());
 | 
						|
					style_.mode = mode::normal;
 | 
						|
					style_.listbox = nullptr;
 | 
						|
				}
 | 
						|
 | 
						|
				void attach(window wd, nana::paint::graphics* graph)
 | 
						|
				{
 | 
						|
					window_ = wd;
 | 
						|
					API::bgcolor(wd, colors::white);
 | 
						|
					graph_ = graph;
 | 
						|
				}
 | 
						|
 | 
						|
				void detach()
 | 
						|
				{
 | 
						|
					window_ = nullptr;
 | 
						|
					graph_ = nullptr;
 | 
						|
				}
 | 
						|
 | 
						|
				window window_handle() const
 | 
						|
				{
 | 
						|
					return window_;
 | 
						|
				}
 | 
						|
 | 
						|
				const container& tree() const
 | 
						|
				{
 | 
						|
					return treebase_;
 | 
						|
				}
 | 
						|
 | 
						|
				container& tree()
 | 
						|
				{
 | 
						|
					return treebase_;
 | 
						|
				}
 | 
						|
 | 
						|
				void draw()
 | 
						|
				{
 | 
						|
					_m_calc_scale();
 | 
						|
 | 
						|
					nana::rectangle r = _m_make_rectangle(); //_m_make_rectangle must be called after _m_calc_scale()
 | 
						|
					_m_calc_pixels(r);
 | 
						|
 | 
						|
					proto_.ui_renderer->background(*graph_, window_, r, ui_el_);
 | 
						|
					if(head_)
 | 
						|
						proto_.ui_renderer->root_arrow(*graph_, _m_make_root_rectangle(), style_.state);
 | 
						|
					_m_draw_items(r);
 | 
						|
					proto_.ui_renderer->border(*graph_);
 | 
						|
				}
 | 
						|
 | 
						|
				bool locate(int x, int y) const
 | 
						|
				{
 | 
						|
					if(graph_)
 | 
						|
					{
 | 
						|
						if(head_)
 | 
						|
						{
 | 
						|
							auto r = _m_make_root_rectangle();
 | 
						|
							if (r.is_hit(x, y))
 | 
						|
							{
 | 
						|
								style_.active_item_rectangle = r;
 | 
						|
								if(ui_el_.what == ui_el_.item_root)
 | 
						|
									return false;
 | 
						|
								ui_el_.what = ui_el_.item_root;
 | 
						|
								return true;
 | 
						|
							}
 | 
						|
						}
 | 
						|
 | 
						|
						nana::rectangle r = _m_make_rectangle();
 | 
						|
						std::vector<node_handle> seq;
 | 
						|
						if(r.is_hit(x, y) && treebase_.seq(head_, seq))
 | 
						|
						{
 | 
						|
							const int xbase = r.x;
 | 
						|
							const int xend = static_cast<int>(r.width) + r.x;
 | 
						|
 | 
						|
							//Change the meaning of variable r. Now, r indicates the area of a item
 | 
						|
							r.height = item_height_;
 | 
						|
 | 
						|
							std::size_t seq_index = 0;
 | 
						|
							for(auto i : seq)
 | 
						|
							{
 | 
						|
								r.width = i->value.second.pixels;
 | 
						|
								//If the item is over the right border of widget, the item would be painted at
 | 
						|
								//the begining of the next line.
 | 
						|
								if(static_cast<int>(r.width) + r.x > xend)
 | 
						|
								{
 | 
						|
									r.x = xbase;
 | 
						|
									r.y += r.height;
 | 
						|
								}
 | 
						|
 | 
						|
								if(r.is_hit(x, y))
 | 
						|
								{
 | 
						|
									style_.active_item_rectangle = r;
 | 
						|
									std::size_t index = seq_index + head_;
 | 
						|
 | 
						|
									ui_element::t what = ((i->child && (r.x + static_cast<int>(r.width) - 16 < x))
 | 
						|
															? ui_el_.item_arrow : ui_el_.item_name);
 | 
						|
									if(what == ui_el_.what && index == ui_el_.index)
 | 
						|
										return false;
 | 
						|
 | 
						|
									ui_el_.what = what;
 | 
						|
									ui_el_.index = index;
 | 
						|
									return true;
 | 
						|
								}
 | 
						|
								r.x += r.width;
 | 
						|
								++seq_index;
 | 
						|
							}
 | 
						|
						}
 | 
						|
					}
 | 
						|
					if(ui_el_.what == ui_el_.somewhere) return false;
 | 
						|
					ui_el_.what = ui_el_.somewhere;
 | 
						|
					return true;
 | 
						|
				}
 | 
						|
 | 
						|
				bool erase_locate()
 | 
						|
				{
 | 
						|
					ui_el_.index = npos;
 | 
						|
					if(ui_el_.what == ui_el_.none)
 | 
						|
						return false;
 | 
						|
 | 
						|
					ui_el_.what = ui_el_.none;
 | 
						|
					return true;
 | 
						|
				}
 | 
						|
 | 
						|
				ui_element locate() const
 | 
						|
				{
 | 
						|
					return ui_el_;
 | 
						|
				}
 | 
						|
 | 
						|
				void mouse_pressed()
 | 
						|
				{
 | 
						|
					style_.state = mouse_action::pressed;
 | 
						|
					switch(ui_el_.what)
 | 
						|
					{
 | 
						|
					case ui_element::item_root:
 | 
						|
					case ui_element::item_arrow:
 | 
						|
						_m_show_list();
 | 
						|
						style_.mode = mode::floatlist;
 | 
						|
						break;
 | 
						|
					default:	//Don't take care about other elements
 | 
						|
						break;
 | 
						|
					}
 | 
						|
				}
 | 
						|
 | 
						|
				void mouse_release()
 | 
						|
				{
 | 
						|
					if(style_.mode != mode::floatlist)
 | 
						|
					{
 | 
						|
						style_.state = mouse_action::normal;
 | 
						|
						if (ui_element::item_name == ui_el_.what)
 | 
						|
							_m_selected(treebase_.tail(ui_el_.index));
 | 
						|
					}
 | 
						|
				}
 | 
						|
 | 
						|
				bool is_list_shown() const
 | 
						|
				{
 | 
						|
					return (nullptr != style_.listbox);
 | 
						|
				}
 | 
						|
 | 
						|
				event_agent_holder& evt_holder() const
 | 
						|
				{
 | 
						|
					return evt_holder_;
 | 
						|
				}
 | 
						|
			private:
 | 
						|
				void _m_selected(node_handle node)
 | 
						|
				{
 | 
						|
					if(node)
 | 
						|
					{
 | 
						|
						API::dev::window_caption(window_handle(), tree().path());
 | 
						|
						if(evt_holder_.selected)
 | 
						|
							evt_holder_.selected(node->value.second.value);
 | 
						|
					}
 | 
						|
				}
 | 
						|
 | 
						|
				void _m_show_list()
 | 
						|
				{
 | 
						|
					if(style_.listbox)
 | 
						|
						style_.listbox->close();
 | 
						|
 | 
						|
					style_.module.items.clear();
 | 
						|
 | 
						|
					nana::rectangle r;
 | 
						|
					style_.list_trigger = ui_el_.what;
 | 
						|
					if(ui_el_.what == ui_el_.item_arrow)
 | 
						|
					{
 | 
						|
						style_.active = ui_el_.index;
 | 
						|
						node_handle i = treebase_.at(ui_el_.index);
 | 
						|
						if(i)
 | 
						|
						{
 | 
						|
							for(node_handle child = i->child; child; child = child->next)
 | 
						|
								style_.module.items.emplace_back(std::make_shared<item>(child->value.first));
 | 
						|
						}
 | 
						|
						r = style_.active_item_rectangle;
 | 
						|
					}
 | 
						|
					else if(ui_el_.item_root == ui_el_.what)
 | 
						|
					{
 | 
						|
						std::vector<node_handle> v;
 | 
						|
						if(treebase_.seq(0, v))
 | 
						|
						{
 | 
						|
							auto end = v.cbegin() + head_;
 | 
						|
							for(auto i = v.cbegin(); i != end; ++i)
 | 
						|
								style_.module.items.emplace_back(std::make_shared<item>((*i)->value.first));
 | 
						|
						}
 | 
						|
						r = style_.active_item_rectangle;
 | 
						|
					}
 | 
						|
					r.y += r.height;
 | 
						|
					r.width = r.height = 100;
 | 
						|
					style_.listbox = &(form_loader<nana::float_listbox>()(window_, r, true));
 | 
						|
					style_.listbox->set_module(style_.module, 16);
 | 
						|
					style_.listbox->events().destroy.connect_unignorable([this]
 | 
						|
					{
 | 
						|
						_m_list_closed();
 | 
						|
					});
 | 
						|
				}
 | 
						|
 | 
						|
				void _m_list_closed()
 | 
						|
				{
 | 
						|
					style_.mode = mode::normal;
 | 
						|
					style_.state = mouse_action::normal;
 | 
						|
 | 
						|
					bool is_draw = false;
 | 
						|
					if((style_.module.index != npos) && style_.module.have_selected)
 | 
						|
					{
 | 
						|
						switch(style_.list_trigger)
 | 
						|
						{
 | 
						|
						case ui_element::item_arrow:
 | 
						|
							{
 | 
						|
								treebase_.tail(style_.active);
 | 
						|
								nana::string name = style_.module.items[style_.module.index]->text();
 | 
						|
								node_handle node = treebase_.find_child(name);
 | 
						|
								if(node)
 | 
						|
								{
 | 
						|
									treebase_.cur(node);
 | 
						|
									_m_selected(node);
 | 
						|
									is_draw = true;
 | 
						|
								}
 | 
						|
							}
 | 
						|
							break;
 | 
						|
						case ui_element::item_root:
 | 
						|
							_m_selected(treebase_.tail(style_.module.index));
 | 
						|
							is_draw = true;
 | 
						|
							break;
 | 
						|
						default:	//Don't take care about other elements
 | 
						|
							break;
 | 
						|
						}
 | 
						|
					}
 | 
						|
					else
 | 
						|
						is_draw = true;
 | 
						|
 | 
						|
					if(is_draw)
 | 
						|
					{
 | 
						|
						draw();
 | 
						|
						API::update_window(window_);
 | 
						|
					}
 | 
						|
					style_.listbox = nullptr;
 | 
						|
				}
 | 
						|
			private:
 | 
						|
				unsigned _m_item_fix_scale() const
 | 
						|
				{
 | 
						|
					return graph_->height() - 2;
 | 
						|
				}
 | 
						|
 | 
						|
				nana::rectangle _m_make_root_rectangle() const
 | 
						|
				{
 | 
						|
					return{ 1, 1, 16, _m_item_fix_scale() };
 | 
						|
				}
 | 
						|
 | 
						|
				//_m_make_rectangle
 | 
						|
				//@brief: This function calculate the items area. This must be called after _m_calc_scale()
 | 
						|
				nana::rectangle _m_make_rectangle() const
 | 
						|
				{
 | 
						|
					nana::rectangle r(1, 1, graph_->width() - 2, _m_item_fix_scale());
 | 
						|
					
 | 
						|
					unsigned px = r.width;
 | 
						|
					std::size_t lines = item_lines_;
 | 
						|
					std::vector<node_handle> v;
 | 
						|
					treebase_.seq(0, v);
 | 
						|
					for(auto node : v)
 | 
						|
					{
 | 
						|
						if(node->value.second.scale.width > px)
 | 
						|
						{
 | 
						|
							if(lines > 1)
 | 
						|
							{
 | 
						|
								--lines;
 | 
						|
								px = r.width;
 | 
						|
								if(px < node->value.second.scale.width)
 | 
						|
								{
 | 
						|
									--lines;
 | 
						|
									continue;
 | 
						|
								}
 | 
						|
							}
 | 
						|
							else
 | 
						|
							{
 | 
						|
								//Too many items, so some of items cann't be displayed
 | 
						|
								r.x += 16;
 | 
						|
								r.width -= 16;
 | 
						|
								return r;
 | 
						|
							}
 | 
						|
						}
 | 
						|
						px -= node->value.second.scale.width;
 | 
						|
					}
 | 
						|
 | 
						|
					return r;
 | 
						|
				}
 | 
						|
 | 
						|
				void _m_calc_scale()
 | 
						|
				{
 | 
						|
					nana::size tsz;
 | 
						|
					unsigned highest = 0;
 | 
						|
					std::vector<node_handle> v;
 | 
						|
					treebase_.seq(0, v);
 | 
						|
					for(auto node : v)
 | 
						|
					{
 | 
						|
						node->value.second.scale = graph_->text_extent_size(node->value.first);
 | 
						|
 | 
						|
						if(highest < node->value.second.scale.height)
 | 
						|
							highest = node->value.second.scale.height;
 | 
						|
 | 
						|
						node->value.second.scale.width += (node->child ? 26 : 10);
 | 
						|
					}
 | 
						|
 | 
						|
					highest += 6; //the default height of item.
 | 
						|
 | 
						|
					item_lines_ = (graph_->height() - 2) / highest;
 | 
						|
					if(item_lines_ == 0)
 | 
						|
						item_lines_ = 1;
 | 
						|
					item_height_ = (1 != item_lines_ ? highest : _m_item_fix_scale());
 | 
						|
				}
 | 
						|
 | 
						|
				void _m_calc_pixels(const nana::rectangle& r)
 | 
						|
				{
 | 
						|
					std::size_t lines = item_lines_;
 | 
						|
 | 
						|
					unsigned px = 0;
 | 
						|
					head_ = 0;
 | 
						|
					std::vector<node_handle> v;
 | 
						|
					treebase_.seq(0, v);
 | 
						|
					for(auto vi = v.rbegin(); vi != v.rend(); ++vi)
 | 
						|
					{
 | 
						|
						item_tag & m = (*vi)->value.second;
 | 
						|
						if(r.width >= px + m.scale.width)
 | 
						|
						{
 | 
						|
							px += m.scale.width;
 | 
						|
							m.pixels = m.scale.width;
 | 
						|
							continue;
 | 
						|
						}
 | 
						|
 | 
						|
						//In fact, this item must be in the font of a line.
 | 
						|
						m.pixels = (r.width >= m.scale.width ? m.scale.width : _m_minimial_pixels());
 | 
						|
						if(0 == px)	//This line is empty, NOT a newline
 | 
						|
						{
 | 
						|
							px = m.pixels;
 | 
						|
							continue;
 | 
						|
						}
 | 
						|
 | 
						|
						//Newline, and check here whether is more lines.
 | 
						|
						if(0 == --lines)
 | 
						|
						{
 | 
						|
							head_ = std::distance(vi, v.rend());
 | 
						|
							break;
 | 
						|
						}
 | 
						|
						px = m.pixels;
 | 
						|
					}
 | 
						|
				}
 | 
						|
 | 
						|
				unsigned _m_minimial_pixels()
 | 
						|
				{
 | 
						|
					return 46;
 | 
						|
				}
 | 
						|
 | 
						|
				void _m_draw_items(const nana::rectangle& r)
 | 
						|
				{
 | 
						|
					nana::rectangle item_r = r;
 | 
						|
					item_r.height = item_height_;
 | 
						|
					std::size_t index = head_;
 | 
						|
					const int xend = static_cast<int>(r.width) + r.x;
 | 
						|
					std::vector<node_handle> v;
 | 
						|
					treebase_.seq(0, v);
 | 
						|
					for(auto vi = v.begin() + head_; vi != v.end(); ++vi)
 | 
						|
					{
 | 
						|
						node_handle i = (*vi);
 | 
						|
						if(static_cast<int>(i->value.second.pixels) + item_r.x > xend)
 | 
						|
						{
 | 
						|
							item_r.x = r.x;
 | 
						|
							item_r.y += item_height_;
 | 
						|
						}
 | 
						|
						item_r.width = i->value.second.pixels;
 | 
						|
						proto_.ui_renderer->item(*graph_, item_r, index++, i->value.first, i->value.second.scale.height, i->child != 0, style_.state);
 | 
						|
						item_r.x += item_r.width;
 | 
						|
					}
 | 
						|
				}
 | 
						|
			private:
 | 
						|
				window	window_{nullptr};
 | 
						|
				nana::paint::graphics * graph_{nullptr};
 | 
						|
				nana::string splitstr_;
 | 
						|
				std::size_t	head_;
 | 
						|
				unsigned	item_height_;
 | 
						|
				std::size_t	item_lines_;
 | 
						|
				container	treebase_;
 | 
						|
 | 
						|
				mutable ui_element	ui_el_;
 | 
						|
				struct style_tag
 | 
						|
				{
 | 
						|
					ui_element::t list_trigger;
 | 
						|
					std::size_t active;	//It indicates the item corresponding listbox.
 | 
						|
					mutable ::nana::rectangle active_item_rectangle;
 | 
						|
					::nana::float_listbox::module_type module;
 | 
						|
					::nana::float_listbox * listbox;
 | 
						|
					scheme::mode	mode;
 | 
						|
					mouse_action	state;	//The state of mouse
 | 
						|
				}style_;
 | 
						|
 | 
						|
				struct proto_tag
 | 
						|
				{
 | 
						|
					pat::cloneable<renderer> ui_renderer;
 | 
						|
				}proto_;
 | 
						|
 | 
						|
				mutable event_agent_holder	evt_holder_;
 | 
						|
			};
 | 
						|
 | 
						|
			//class trigger
 | 
						|
				trigger::trigger()
 | 
						|
					: scheme_(new scheme)
 | 
						|
				{}
 | 
						|
 | 
						|
				trigger::~trigger()
 | 
						|
				{
 | 
						|
					delete scheme_;
 | 
						|
				}
 | 
						|
 | 
						|
				void trigger::insert(const nana::string& str, nana::any value)
 | 
						|
				{
 | 
						|
					scheme_->tree().insert(str, value);
 | 
						|
					API::dev::window_caption(scheme_->window_handle(), scheme_->tree().path());
 | 
						|
					scheme_->draw();
 | 
						|
				}
 | 
						|
 | 
						|
				bool trigger::childset(const nana::string& str, nana::any value)
 | 
						|
				{
 | 
						|
					if(scheme_->tree().childset(str, value))
 | 
						|
					{
 | 
						|
						scheme_->draw();
 | 
						|
						return true;
 | 
						|
					}
 | 
						|
					return false;
 | 
						|
				}
 | 
						|
 | 
						|
				bool trigger::childset_erase(const nana::string& str)
 | 
						|
				{
 | 
						|
					if(scheme_->tree().childset_erase(str))
 | 
						|
					{
 | 
						|
						scheme_->draw();
 | 
						|
						return true;
 | 
						|
					}
 | 
						|
					return false;
 | 
						|
				}
 | 
						|
 | 
						|
				bool trigger::clear()
 | 
						|
				{
 | 
						|
					if(scheme_->tree().clear())
 | 
						|
					{
 | 
						|
						scheme_->draw();
 | 
						|
						return true;
 | 
						|
					}
 | 
						|
					return false;
 | 
						|
				}
 | 
						|
 | 
						|
				void trigger::splitstr(const nana::string& sstr)
 | 
						|
				{
 | 
						|
					scheme_->tree().splitstr(sstr);
 | 
						|
				}
 | 
						|
 | 
						|
				const nana::string& trigger::splitstr() const
 | 
						|
				{
 | 
						|
					return scheme_->tree().splitstr();
 | 
						|
				}
 | 
						|
 | 
						|
				void trigger::path(const nana::string& str)
 | 
						|
				{
 | 
						|
					scheme_->tree().path(str);
 | 
						|
				}
 | 
						|
 | 
						|
				nana::string trigger::path() const
 | 
						|
				{
 | 
						|
					return scheme_->tree().path();
 | 
						|
				}
 | 
						|
 | 
						|
				nana::any& trigger::value() const
 | 
						|
				{
 | 
						|
					auto node = scheme_->tree().cur();
 | 
						|
					if(node)
 | 
						|
						return node->value.second.value;
 | 
						|
 | 
						|
					throw std::runtime_error("Nana.GUI.categorize::value(), current category is empty");
 | 
						|
				}
 | 
						|
 | 
						|
				void trigger::_m_event_agent_ready() const
 | 
						|
				{
 | 
						|
					auto evt_agent = event_agent_.get();
 | 
						|
					scheme_->evt_holder().selected = [evt_agent](::nana::any& val){
 | 
						|
						evt_agent->selected(val);
 | 
						|
					};
 | 
						|
				}
 | 
						|
 | 
						|
				void trigger::attached(widget_reference widget, graph_reference graph)
 | 
						|
				{
 | 
						|
					scheme_->attach(widget, &graph);
 | 
						|
				}
 | 
						|
 | 
						|
				void trigger::detached()
 | 
						|
				{
 | 
						|
					scheme_->detach();
 | 
						|
				}
 | 
						|
 | 
						|
				void trigger::refresh(graph_reference)
 | 
						|
				{
 | 
						|
					scheme_->draw();
 | 
						|
				}
 | 
						|
 | 
						|
				void trigger::mouse_down(graph_reference, const arg_mouse&)
 | 
						|
				{
 | 
						|
					if(scheme_->locate().what > ui_element::somewhere)
 | 
						|
					{
 | 
						|
						if(API::window_enabled(scheme_->window_handle()))
 | 
						|
						{
 | 
						|
							scheme_->mouse_pressed();
 | 
						|
							scheme_->draw();
 | 
						|
							API::lazy_refresh();
 | 
						|
						}
 | 
						|
					}
 | 
						|
				}
 | 
						|
 | 
						|
				void trigger::mouse_up(graph_reference, const arg_mouse&)
 | 
						|
				{
 | 
						|
					if(scheme_->locate().what > ui_element::somewhere)
 | 
						|
					{
 | 
						|
						if(API::window_enabled(scheme_->window_handle()))
 | 
						|
						{
 | 
						|
							scheme_->mouse_release();
 | 
						|
							scheme_->draw();
 | 
						|
							API::lazy_refresh();
 | 
						|
						}
 | 
						|
					}
 | 
						|
				}
 | 
						|
 | 
						|
				void trigger::mouse_move(graph_reference, const arg_mouse& arg)
 | 
						|
				{
 | 
						|
					if(scheme_->locate(arg.pos.x, arg.pos.y) && API::window_enabled(scheme_->window_handle()))
 | 
						|
					{
 | 
						|
						scheme_->draw();
 | 
						|
						API::lazy_refresh();
 | 
						|
					}
 | 
						|
				}
 | 
						|
 | 
						|
				void trigger::mouse_leave(graph_reference, const arg_mouse&)
 | 
						|
				{
 | 
						|
					if(API::window_enabled(scheme_->window_handle()) && (scheme_->is_list_shown() == false) && scheme_->erase_locate())
 | 
						|
					{
 | 
						|
						scheme_->draw();
 | 
						|
						API::lazy_refresh();
 | 
						|
					}
 | 
						|
				}
 | 
						|
			//end class trigger
 | 
						|
		}//end namespace categorize
 | 
						|
	}//end namespace draerbase
 | 
						|
}//end namespace nana
 |