[SOLVED] PGUI Positioning

Hello everyone,
Im trying to make a custom window class as subclass of PGButton but there is something wrong in my code about moving part (Positioning / Coords).
I’m not very familiar with the 2DAspect coordinate system and cant see the problem.
I just wanted to ask, do you have any idea?

GUIWindow.cpp;

#include "GUIWindow.h"


void OnMouseDown(const Event* pTheEvent, void* pData)
{
	auto self = static_cast<GUIWindow*>(pData);
	if (!self) { return; }
	const LPoint2 mpos = self->mwatcher->get_mouse();
	self->mouse_x_delta = abs(mpos.get_x() - self->window_np.get_pos().get_x());
	self->mouse_y_delta = abs(mpos.get_y() - self->window_np.get_pos().get_y());
	self->is_moving = true;
	std::cout << "OnMouseDown" << std::endl;
}

void OnMouseUp(const Event* pTheEvent, void* pData)
{
	static_cast<GUIWindow*>(pData)->is_moving = false;
	std::cout << "OnMouseUp" << std::endl;
}

GUIWindow::~GUIWindow()
{

}

void GUIWindow::initEvents()
{
	Engine* engine = &Engine::instance();
	engine->framework.get_task_mgr().add(make_task([=](AsyncTask* task) -> AsyncTask::DoneStatus {
		if (is_moving) { OnMove(); }
		return AsyncTask::DS_cont;
		}, "OnMove"));
	engine->framework.define_key(this->get_press_event(MouseButton::one()), "button down", OnMouseDown, this);
	engine->framework.define_key(this->get_release_event(MouseButton::one()), "button up", OnMouseUp, this);
}

void GUIWindow::OnMove()
{
	if (!mwatcher->has_mouse()) { return; }
	const LPoint2 mpos = mwatcher->get_mouse();
	window_np.set_pos(mpos.get_x() - mouse_x_delta, 0, mpos.get_y() - mouse_y_delta);
	std::cout << "OnMove.." << std::endl;
}

GUIWindow.h;

#pragma once
#include <stdio.h>
#include <asyncTaskManager.h>
#include <genericAsyncTask.h>
#include <texturePool.h>
#include "mouseWatcher.h"
#include "pgButton.h"

#include "Engine.h"
#include <mouseButton.h>

template<class Callable>
AsyncTask* make_task(Callable callable, const std::string& name, int sort = 0, int priority = 0) {
	class InlineTask final : public AsyncTask {
	public:
		InlineTask(Callable callable, const std::string& name, int sort, int priority) :
			AsyncTask(name),
			_callable(std::move(callable)) {
			_sort = sort;
			_priority = priority;
		}

		ALLOC_DELETED_CHAIN(InlineTask);

	private:
		virtual DoneStatus do_task() override final {
			return _callable(this);
		}

		Callable _callable;
	};
	return new InlineTask(std::move(callable), name, sort, priority);
}

class GUIWindow : public PGButton
{
public:
	GUIWindow(const std::string tex_path, float _scale_factor=1.0):PGButton("GUIWindow"){
		Engine* engine = &Engine::instance();
		tex = TexturePool::load_texture(tex_path);
		tex->set_anisotropic_degree(0);
		width = tex->get_orig_file_x_size();
		height = tex->get_orig_file_y_size();
		scale_factor = _scale_factor;

		PGFrameStyle _style = this->get_frame_style(0);
		_style.set_color(1.0f, 1.0f, 1.0f, 0.95f);
		_style.set_type(PGFrameStyle::T_flat);
		_style.set_texture(tex);

		this->clear_effects();
		this->setup("", .0);
		this->set_notify(NULL);
		this->set_frame_style(0, _style);
		this->set_frame_style(1, _style);
		this->set_frame_style(2, _style);
		this->set_frame_style(3, _style);
		this->set_frame(((float)width / (float)height) * scale_factor, 0, 0, 1 * scale_factor);

		window_np = engine->window->get_aspect_2d().attach_new_node(this);
		mwatcher = DCAST(MouseWatcher, engine->window->get_mouse().node());

		initEvents();
	}

	~GUIWindow();

	void initEvents();
	void OnMove();

	bool is_show;
	bool is_moving;
	int width;
	int height;
	float scale_factor;
	float mouse_x_delta;
	float mouse_y_delta;
	Texture* tex;
	NodePath window_np;
	MouseWatcher* mwatcher;
};
GUIWindow* test_window = new GUIWindow("bin/neuera/ui/window_bg_small.png");

Video;

Caveat: I’m not all that familiar with the C++ side of Panda; it’s possible that there’s a difference here that I’m missing. So, take with a pinch of salt.

I’m not sure offhand of why your window jumps when you click on it–there’s an offset being misapplied or incorrectly not applied, perhaps–but I can perhaps speak to why your window moves less than the mouse:

Presuming that the mouse-coordinates on the C++ side are much as they are on the Python side–and I’d imagine that they are–mouse-coordinates are given in the range (-1, 1).

However, the coordinates of aspect2d are different to that, having a scalar applied such that aspect2d reflects the aspect ratio of the window. For example, if the window is wider than it is tall (as is usual these days), then the sides of the screen will have x-coordinates with an absolute value greater than 1.

To deal with this, I think that you could either multiply your mouse-coordinates by the scale of aspect2d when placing the window, or simply place the window relative to render2d.

2 Likes

@Thaumaturge yes like u said, aspect coordinates changing with aspect-ratio.
To solve problem i add some convert functions;

LPoint2 MouseToGlobalPos(float x, float y){
	LPoint2 result;
	result.set_x(((x + 1.0f) / 2.0f) * 800);
	result.set_y(((-y + 1.0f) / 2.0f) * 600);
	return result;
}

LVecBase3 GlobalToAspectPos(float x, float y)
{
	return LVecBase3((PN_stdfloat)((2 * ((double)x / (double)600)) - ((double)800 / (double)600)), (PN_stdfloat)0, (PN_stdfloat)(1 - (2 * (double)y / (double)600)));
}

LVecBase3 MouseToAspectPos(float _x, float _y)
{
	LPoint2 global_mpos = MouseToGlobalPos(_x, _y);
	return GlobalToAspectPos(global_mpos.get_x(), global_mpos.get_y());
}

And change the code a bit;

void OnMouseDown(const Event* pTheEvent, void* pData)
{
	auto self = static_cast<GUIWindow*>(pData);
	if (!self) { return; }
	const LPoint2 mpos = self->mwatcher->get_mouse();
	const LVecBase3 apos = MouseToAspectPos(mpos.get_x(), mpos.get_y());
	self->mouse_x_delta = abs(apos.get_x() - self->window_np.get_pos().get_x());
	self->mouse_y_delta = abs(apos.get_z() - self->window_np.get_pos().get_z());
	self->is_moving = true;
}

void GUIWindow::OnMove()
{
	if (!mwatcher->has_mouse()) { return; }
	const LPoint2 mpos = mwatcher->get_mouse();
	LPoint2 gmpos = MouseToGlobalPos(mpos.get_x(), mpos.get_y());
	LVecBase3 next_pos = GlobalToAspectPos(gmpos.get_x(), gmpos.get_y());
	next_pos.add_x(-1 * mouse_x_delta);
	next_pos.add_z(-1 * mouse_y_delta);
	window_np.set_pos(next_pos);
	std::cout << "MOUSE:" << mpos << std::endl;
	std::cout << "GLOBAL MOUSE:" << gmpos << std::endl;
	std::cout << "NEXT:" << next_pos << std::endl;
	std::cout << "DELTA X:" << mouse_x_delta << std::endl;
	std::cout << "DELTA Y:" << mouse_y_delta << std::endl;
}

And it worked like expected;

2 Likes

Well done! :slight_smile:

Although note that your code will, I believe, only work for an 800 x 600 window; if you instead draw on the scaling-factor of aspect2d, it should work for any window-size.

1 Like

Yes, im just testing with 800x600 i’ll change it to suitable :slight_smile:
Im just wondering, if there is someting wrong with using PGButton as baseclass of window? Because i’ll attach some other nodes to window that means attach buttons to button :thinking:

1 Like

To the former, fair enough! :slight_smile:

To the latter, honestly, I don’t much work on the C++ side, and thus don’t really touch PGUI, so I don’t have an answer for you there–I’ll let someone else (hopefully) give an answer!

1 Like