Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multi selection support for treebox #691

Closed
edgarbernal5 opened this issue Feb 21, 2024 · 6 comments
Closed

Multi selection support for treebox #691

edgarbernal5 opened this issue Feb 21, 2024 · 6 comments
Assignees

Comments

@edgarbernal5
Copy link

Hi! Just wondering if we can add support for multi selection as already is with listbox, don't know if you have plans to do it soon, and if not can you give me an insight of it, where can I start from?
Thanks in advance!

@edgarbernal5
Copy link
Author

edgarbernal5 commented Feb 22, 2024

Hi again! After debugging and modifying the code, I am able to add support for multiselect. I added Toggle and Replace Select (ctrl and shift key modifiers respectively).

To sum up:

  • Added std::vector<node_type*> nodes_selected in node_state_tag to push selected node in the fly.
  • Added helpers funcitons to manage that collection of nodes.
  • node_type * selected now means "last selected node"
  • Implemented set_selected_multi function for handling Toggle and Replace selection. Here is the code:
bool set_selected_multi(node_type* node, bool crtl, bool shift)
{
	if (crtl)
	{
		data.stop_drawing = true;
		if (node_state.is_selected_node(node))
		{
			node_state.remove_node(node);
			item_proxy iprx(data.trigger_ptr, node);
			data.widget_ptr->events().selected.emit(::nana::arg_treebox{ *data.widget_ptr, iprx, false }, data.widget_ptr->handle());
		}
		else
		{
			node_state.add_node(node);
			item_proxy iprx(data.trigger_ptr, node);
			data.widget_ptr->events().selected.emit(::nana::arg_treebox{ *data.widget_ptr, iprx, true }, data.widget_ptr->handle());
		}
		data.stop_drawing = false;

		node_state.selected = node;
		return true;
	}

	data.stop_drawing = true;
	if (node_state.selected)
	{
		//node_state.selected through node

		node_state.clear();
		auto target1 = node_state.selected;
		auto target2 = node;
		item_proxy{ data.trigger_ptr, attr.tree_cont.get_root() }.visit_recursively([&](item_proxy&& i)
		{
			if (i.empty())
			{
				return false;
			}
			else if ((!i.child().empty() && !i.expanded()))
			{
				if ((target1 == nullptr || target2 == nullptr) && (i._m_node() == target1 || i._m_node() == target2))
				{
					node_state.add_node(i._m_node());
					return false;
				}
			}

			if ((target1 == nullptr || target2 == nullptr) && (i._m_node() == target1 || i._m_node() == target2))
			{
				node_state.add_node(i._m_node());
				return false;
			}

			if (i._m_node() == node_state.selected)
			{
				target1 = nullptr;
				node_state.add_node(i._m_node());
			}
			else if (i._m_node() == node)
			{
				target2 = nullptr;
				node_state.add_node(i._m_node());
			}
			else if ((target1 == nullptr || target2 == nullptr))
			{
				if (i.hidden())
					return false;

				node_state.add_node(i._m_node());
			}
			return true;
		});

		for (auto& sel_node : node_state.nodes_selected)
		{
			item_proxy iprx(data.trigger_ptr, sel_node);
			data.widget_ptr->events().selected.emit(::nana::arg_treebox{ *data.widget_ptr, iprx, true }, data.widget_ptr->handle());
		}
	}
	else
	{
		node_state.add_node(node);
		item_proxy iprx(data.trigger_ptr, node);
		data.widget_ptr->events().selected.emit(::nana::arg_treebox{ *data.widget_ptr, iprx, true }, data.widget_ptr->handle());
	}

	node_state.selected = node;
	
	data.stop_drawing = false;
	return true;
}

Existing set_selected function was modified to manage selected node vector:

bool set_selected(node_type * node)
{
	if(node_state.selected != node)
	{
		data.stop_drawing = true;
		if (node_state.selected)
		{
			auto copy_nodes = node_state.nodes_selected;
			node_state.clear();
			for (auto& selected_node : copy_nodes)
			{
				item_proxy iprx(data.trigger_ptr, selected_node);
				data.widget_ptr->events().selected.emit(::nana::arg_treebox{ *data.widget_ptr, iprx, false }, data.widget_ptr->handle());
			}
		}

		node_state.selected = node;
		if (node)
		{
			node_state.add_node(node_state.selected);
			item_proxy iprx(data.trigger_ptr, node_state.selected);
			data.widget_ptr->events().selected.emit(::nana::arg_treebox{ *data.widget_ptr, iprx, true }, data.widget_ptr->handle());
		}
		data.stop_drawing = false;
		return true;
	}
	return false;
}

Finally to know if a node is selected before rendering it, modified node attribute for selected field:
ndattr.selected = (node_state.is_selected_node(node));

And that's pretty much of it. Want to know what do you think guys? @cnjinhao @qPCR4vir.
Thanks for your time!

@cnjinhao
Copy link
Owner

Hi, @edgarbernal5 , good job!

@edgarbernal5
Copy link
Author

Thanks!!! I created the PR #693

@edgarbernal5
Copy link
Author

edgarbernal5 commented Mar 27, 2024

How do we use this new feature?

In the client side, set the flag to true
m_treebox.enable_multiselection(true);

I just added two new methods in treebox:

void treebox::deselect_all()
{
	auto dw = &get_drawer_trigger();
	bool should_draw = dw->impl()->node_state.nodes_selected.size() > 0;
	dw->impl()->node_state.deselect_all();
	if (should_draw)
		dw->impl()->draw(false);
}

and this:

void treebox::selected(std::vector<treebox::item_proxy>& selected_nodes) const
{
	auto dw = &get_drawer_trigger();
	for (auto& item : dw->impl()->node_state.nodes_selected)
	{
		selected_nodes.push_back({ const_cast<drawer_trigger_t*>(dw) , item });
	}
}

@cnjinhao
Copy link
Owner

Look forward to your PR.

@edgarbernal5
Copy link
Author

Nice!! The PR is created #694.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants