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

Drag/droppable TreeNodes with custom images #5473

Closed
unsettledgames opened this issue Jul 14, 2022 · 6 comments
Closed

Drag/droppable TreeNodes with custom images #5473

unsettledgames opened this issue Jul 14, 2022 · 6 comments

Comments

@unsettledgames
Copy link

Version/Branch of Dear ImGui:

Version: 1.88
Branch: docking

Back-end/Renderer/Compiler/OS

backends/imgui_impl_glfw.cpp
backends/imgui_impl_opengl3.cpp
Compiler: /
Operating System: Windows 10

My Issue/Question:

Hello! I'm building a scene hierarchy in my graphics engine and I'd like each TreeNode to be drag and droppable (files, which are leaves of the hierarchy, can be dragged and dropped to be referenced by other panels, other nodes should also be drag/droppable because in the case of 3D models, I'd like to show the list of meshes they're made of and let users swap meshes using a different window).

In addition, I'd like each TreeNode to have a small icon on the left to represent the type of file (folder, texture, mesh etc).

I read #282 and I managed to render icons on the left, but I can't get drag and drop to work by just calling ImGui::BeginDragDropSource,

A workaround that let me drag and drop leaves until now consisted in adding an overlapping ImGui::InvisibleButton on them and then calling ImGui::BeginDragDropSource. This, however, doesn't work for nodes that could be expanded because the overlapping InvisibleButton prevents them from opening.

Screenshots/Video

image

Standalone, minimal, complete and verifiable example:

// Here's some code anyone can copy and paste to reproduce your issue
void ContentBrowserPanel::DrawHierarchy(std::filesystem::path path, bool isDir)
	{
		Ref<Texture2D> icon = isDir ? EditorCache::Textures().Get("cb-directory") : GetFileIcon(path);
		ImGuiTreeNodeFlags treeNodeFlags = ImGuiTreeNodeFlags_AllowItemOverlap
			| ImGuiTreeNodeFlags_Framed | ImGuiTreeNodeFlags_SpanAvailWidth | ImGuiTreeNodeFlags_FramePadding;
		auto openFolder = std::find(m_OpenDirs.begin(), m_OpenDirs.end(), path.filename().string());

		// Open the current node if it's been opened
		if (openFolder != m_OpenDirs.end())
			treeNodeFlags |= ImGuiTreeNodeFlags_DefaultOpen;


		// Use invisible buttons
		ImVec2 cursorPos = ImGui::GetCursorPos();
		// Render the tree node
		ImGui::SetItemAllowOverlap();
		if (ImGuiUtils::ImageTreeNode(path.filename().string().c_str(), (ImTextureID)icon->GetRendererID()))
		{
			// Toggle the current node if it's been clicked
			if (openFolder != m_OpenDirs.end())
				m_OpenDirs.erase(openFolder);
			else
				m_OpenDirs.push_back(path.filename().string());

			for (auto& dirEntry : std::filesystem::directory_iterator(path))
			{
				const std::filesystem::path& otherPath = dirEntry.path();
				// Don't show meta files
				if (otherPath.extension().string() == ".meta")
					continue;

				DrawHierarchy(otherPath, dirEntry.is_directory());
			}

			ImGui::TreePop();
		}
		ImVec2 finalCursorPos = ImGui::GetCursorPos();
		ImVec2 size = ImGui::GetItemRectSize();

		if (!isDir)
		{
			ImGui::SetCursorPos(cursorPos);
			ImGui::SetItemAllowOverlap();

			if (ImGui::InvisibleButton(("##" + path.string()).c_str(), size))
			{
				// Set properties panel path
				m_PropertiesPanel->SetAsset(path);
			}

			AddDragSource(path);

			ImGui::SetCursorPos(finalCursorPos);
		}
		
	}

void ContentBrowserPanel::AddDragSource(const std::filesystem::path path)
	{
		if (ImGui::BeginDragDropSource())
		{
			const wchar_t* itemPath = path.c_str();
			ImGui::SetDragDropPayload("CONTENT_BROWSER_DATA", itemPath, (wcslen(itemPath) + 1) * sizeof(wchar_t), ImGuiCond_Once);
			ImGui::EndDragDropSource();
		}
	}
@rokups
Copy link
Contributor

rokups commented Jul 15, 2022

You do not need InvisibueButton(), drag&drop works fine with tree nodes.

bool is_tree_open = ImGui::TreeNode("Foo");
if (ImGui::BeginDragDropSource())
{
    ImGui::TextUnformatted("Foo");
    ImGui::EndDragDropSource();
}

if (is_tree_open)
{
    is_tree_open = ImGui::TreeNode("Bar");
    if (ImGui::BeginDragDropSource())
    {
        ImGui::TextUnformatted("Bar");
        ImGui::EndDragDropSource();
    }
    if (is_tree_open)
    {
        ImGui::TreePop();
    }
    ImGui::TreePop();
}

If you are still having a problem try making a small self-contained reproduction of the issue that could be pasted to one of example apps and tested that way.

@unsettledgames
Copy link
Author

Thanks for the answer! I couldn't get the above approach to work because the code in #282 uses the internal API and apparently it didn't support BeginDragDropSource.

However, I fiddled with it a bit more and got it to work without invisible buttons, so there must've been some issues with my own code.

Thank you again and great job for the library & support, I'll close this.

@rokups
Copy link
Contributor

rokups commented Jul 15, 2022

You must be rendering icons manually, which is quite cumbersome with trees (i have been down this path). A simpler way would be using icons font, if you can find one with good enough icons. Then icons become part of the text and its trivial to insert them anywhere.

@unsettledgames
Copy link
Author

Yeah I considered icon fonts but couldn't find one I liked enough, plus I'm terrible at vector design, so I went down the other path. Was probably less trivial than icon fonts, but I still managed to make it work without too mny troubles.

@PathogenDavid
Copy link
Contributor

plus I'm terrible at vector design

You don't need to actually put your icons into an icon font. You can manually populate custom characters with a bitmap as well, see this section of the fonts documentation for details.

@unsettledgames
Copy link
Author

Thanks a lot! Will have a look.

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

No branches or pull requests

3 participants