Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: [solved] Dragging with mouse  (Read 9498 times)

0 Members and 1 Guest are viewing this topic.

SLPPOrchestra

  • Newbie
  • *
  • Posts: 5
    • View Profile
[solved] Dragging with mouse
« on: April 16, 2011, 08:43:19 pm »
I'm trying to create a drag and drop effect with a sf::Drawable object. It will only work in a small region around the object (~50 window pixels). Any help? (Using SFML C++ 2.0 btw)

Code: [Select]

/* virtual */ void HUD::Input(const sf::Input &input)
{
static sf::Vector2f startMousePos = sf::Vector2f(0.0f, 0.0f);
if(_dragged)
{
if(!_isDragging)
{
startMousePos = App::GetInst().GetWindow().ConvertCoords(
input.GetMouseX(), input.GetMouseY());
startMousePos -= _dragged->GetPosition();
_isDragging = true;
}
sf::Vector2f curMousePos = App::GetInst().GetWindow().ConvertCoords(
input.GetMouseX(), input.GetMouseY());
_dragged->SetPosition(curMousePos - startMousePos);
}
}

Wizzard

  • Full Member
  • ***
  • Posts: 213
    • View Profile
[solved] Dragging with mouse
« Reply #1 on: April 17, 2011, 12:06:44 pm »
I don't think you've posted enough code for people to help you. You should post a commented and compilable, albeit minimal, example of what you're trying to do.

SLPPOrchestra

  • Newbie
  • *
  • Posts: 5
    • View Profile
[solved] Dragging with mouse
« Reply #2 on: April 17, 2011, 05:48:29 pm »
Hmm ok...I tried to pick what I though is important. Clearly this isn't compilable, but there are a lot of helper classes.

Button.hpp
Code: [Select]

class Button : public sf::Drawable, public toj::IInput // just know that an IInput will receive input notifications
{
public:
Button(const std::wstring &label = L"", sf::Vector2f pos = sf::Vector2f(0, 0),
sf::Vector2f size = sf::Vector2f(0, 0));

void SetSize(const sf::Vector2f &size) { _size = size; }
void SetLabel(const std::wstring &label) { _label.SetString(label); }
void SetOnClick(Functor &onClick) { _onClick = &onClick; }
void SetOnClickStop(Functor &onClickStop) { _onClickStop = &onClickStop; }
void SetOnHover(Functor &onHover) { _onHover = &onHover; }
void SetOnHoverExit(Functor &onHoverExit) { _onHoverExit = &onHoverExit; }
void ResetPosition() { SetPosition(_startPos); }
private:
virtual void Render(sf::RenderTarget &target, sf::Renderer &renderer) const;
virtual void Input(const sf::Input &input);

Functor *_onClick;
Functor *_onClickStop;
Functor *_onHover;
Functor *_onHoverExit;

sf::Text _label;

sf::Vector2f _size;
sf::Vector2f _startPos;
bool _mouseHovering;
bool _mouseClicking;
};


Button.cpp
Code: [Select]

/* virtual */ void Button::Input(const sf::Input &input)
{
sf::RenderWindow &window = App::GetInst().GetWindow();
sf::Vector2f mouseCoords = TransformToLocal(window.ConvertCoords(input.GetMouseX(), input.GetMouseY())); // Is there a better way to get mouse coords in...scene view?

mouseCoords.x -= GetPosition().x;
mouseCoords.y -= GetPosition().y;

sf::FloatRect thisRect(0, 0, _size.x, _size.y);
if(thisRect.Contains(mouseCoords.x, mouseCoords.y))
{
if(input.IsMouseButtonDown(sf::Mouse::Left))
{
if(_onClick)
(*_onClick)();
_mouseClicking = true;
}
else
{
if(_onClickStop && _mouseClicking)
(*_onClickStop)();
if(_onHover && !_mouseHovering)
(*_onHover)();
_mouseClicking = false;
}
_mouseHovering = true;
}
else
{
if(_onClickStop && _mouseClicking)
(*_onClickStop)();
if(_onHoverExit && _mouseHovering)
(*_onHoverExit)();
_mouseHovering = false;
_mouseClicking = false;
}
}


HUD.hpp
Code: [Select]

class HUD : public sf::Drawable, public toj::IInputObjectParent, public toj::IInput
{
class ButtonClickHandler : public toj::Functor // Click handler will call StartDrag with a button reference
class ButtonClickStopHandler : public toj::Functor // Click stop handler will call EndDrag
public:
toj::Button test;
HUD();
private:
virtual void Render(sf::RenderTarget &target, sf::Renderer &renderer) const;
virtual std::vector<toj::IInput*> GetInputObjects();
virtual void Input(const sf::Input &input);

void StartDrag(toj::Button &btn) { _dragged = &btn; }
void EndDrag();

toj::Button *_dragged;
bool _isDragging;
};


HUD.cpp
Code: [Select]

HUD::HUD() : _onClickTest(this, test), _onClickStop(this),
_dragged(NULL), _isDragging(false),
test(L"dzsd", sf::Vector2f(50.0f, 250.0f), sf::Vector2f(100.0f, 100.0f)) // I don't understand positioning...this appears perfectly flush with...
{
_rect = sf::Shape::Rectangle(0.0f, 500.0f, 800.0f, 600.0f, sf::Color(190, 190, 190)); // ...this, but there's a 250px difference in the y-val
test.SetOnClick(_onClickTest);
test.SetOnClickStop(_onClickStop);
}

void HUD::EndDrag()
{
if(_dragged)
{
_dragged->ResetPosition();
_dragged = NULL;
}
_isDragging = false;
}

/* virtual */ void HUD::Render(sf::RenderTarget &target, sf::Renderer &renderer) const
{
target.Draw(_rect);
target.Draw(test);
}

/* virtual */ std::vector<IInput*> HUD::GetInputObjects()
{
std::vector<IInput*> objects;
objects.push_back(&test); // The button gets input to determine what the mouse is doing to it (i.e. clicking, hovering)
objects.push_back(this); // HUD gets input to manipulate button based on whether or not it's being dragged (input code in 1st post)
return objects;
}


I hope this is enough. I guess I could upload the entire VS2010 project somewhere. I could make a video if that would help.

Wizzard

  • Full Member
  • ***
  • Posts: 213
    • View Profile
[solved] Dragging with mouse
« Reply #3 on: April 18, 2011, 07:00:31 am »
There is two reasons on why I ask you to write a compilable, minimal example. The first being that you may, while writing new code, discover something you did wrong previously. If that's not the case, then secondly, it's easy for me to go in and run the new code and edit if it's not bloated with all the unimportant bits to your problem. It's very hard for me to look at portions of an entire project and think about how to theoretically fix a small problem. I need to know exactly what is happening and I feel as if going through your whole project to learn exactly what is happening instead of a small example is a waste of my time. I hope you can understand. If you reproduce your drag and drop problem in a minimal example, I can make every effort to help you out.

Edit:
Also, in your example, I don't even see any code that moves a sf::Shape. It's mainly internal state changes and functions that call your custom functions that I don't know what do except for inferring from their names. There isn't enough here for me to even recreate your method of dragging sf::Shapes around.

SLPPOrchestra

  • Newbie
  • *
  • Posts: 5
    • View Profile
[solved] Dragging with mouse
« Reply #4 on: April 18, 2011, 04:55:49 pm »
OK, this is a much better example.
Code: [Select]

class DragDropTest : public sf::Drawable
{
public:
DragDropTest()
{
_thisRect = sf::FloatRect(100.0f, 100.0f, 100.0f, 100.0f);
_rect = sf::Shape::Rectangle(_thisRect, sf::Color::Blue);
}
void Input(const sf::Input &input)
{
static bool dragging = false;
static sf::Vector2f startPos = sf::Vector2f(0.0f, 0.0f);

sf::Vector2f pos = sf::Vector2f((float)input.GetMouseX(), (float)input.GetMouseY());

if(_thisRect.Contains(pos))
{
if(input.IsMouseButtonDown(sf::Mouse::Left))
{
if(!dragging)
startPos = pos;
dragging = true;
}
else
dragging = false;
}
else
dragging = false;

if(dragging)
SetPosition(pos.x - startPos.x, pos.y - startPos.y);
}
private:
virtual void Render(sf::RenderTarget &target, sf::Renderer &renderer) const
{
target.Draw(_rect);
}

sf::Shape _rect;
sf::FloatRect _thisRect;
};

int main()
{
sf::RenderWindow app(sf::VideoMode(800, 600), "SFML Test");
app.SetFramerateLimit(30);
DragDropTest test;
sf::Event evt;
while(app.IsOpened())
{
while(app.PollEvent(evt))
if(evt.Type == evt.Closed)
app.Close();
app.Clear();
test.Input(app.GetInput());
app.Draw(test);
app.Display();
}
}

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32498
    • View Profile
    • SFML's website
    • Email
[solved] Dragging with mouse
« Reply #5 on: April 18, 2011, 06:10:27 pm »
Quote
It will only work in a small region around the object (~50 window pixels)

Code: [Select]
_thisRect = sf::FloatRect(100.0f, 100.0f, 100.0f, 100.0f);
 
[...]

if(_thisRect.Contains(pos))
    ...
else
    dragging = false;

Looks normal to me ;)
Laurent Gomila - SFML developer

SLPPOrchestra

  • Newbie
  • *
  • Posts: 5
    • View Profile
[solved] Dragging with mouse
« Reply #6 on: April 18, 2011, 06:54:13 pm »
Oops! But now with
Code: [Select]

sf::Vector2f pos = sf::Vector2f((float)input.GetMouseX(), (float)input.GetMouseY());

_thisRect = sf::FloatRect(GetPosition().x, GetPosition().y, 100.0f, 100.0f);
if(_thisRect.Contains(pos))

I can drag but not actually on the shape (i.e. I click off to the side and it will drag). How do I ensure the mouse is actually on the shape?

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32498
    • View Profile
    • SFML's website
    • Email
[solved] Dragging with mouse
« Reply #7 on: April 18, 2011, 07:15:09 pm »
First, your shape's points are starting at position (100, 100) but the shape's global position is (0, 0) -- the points' local position must not be confused with the global shape's position. You should create your shape with points at (0, 0) and then use SetPosition to move it.

Then, you compare the cursor position, which is in window coordinates, to the shape, which is in "world" coordinates. They won't match anymore if you resize the window or use a custom view. To convert mouse coordinates to world coordinates, use the window.ConvertCoords function.

Then your test should succeed ;)
Laurent Gomila - SFML developer

SLPPOrchestra

  • Newbie
  • *
  • Posts: 5
    • View Profile
[solved] Dragging with mouse
« Reply #8 on: April 18, 2011, 07:43:46 pm »
Awesome, thanks Laurent.