Author Topic: Scrollers and Buttons  (Read 9287 times)

jjsanchezramirez

  • Guest
Scrollers and Buttons
« on: October 02, 2012, 02:38:40 am »
Ever wanted to create scrolling menus in DragonFireSDK? Now you can.

I created two classes, one called Scroller and another called ScrollButton. Scroller creates scrollers with elasticity and friction. ScrollButton creates buttons for the scroller. If you don't move, you click the button. If you move, you don't click the button and drag the scroller.



Watch the video here:
http://www.youtube.com/watch?v=J1UELzjk0XE&feature=g-upl

Scroller.h
Code: [Select]
/* Copyright 2012 Juan José Sánchez Ramírez

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.

   Author Juan José Sánchez Ramírez
   Co-author Nico91 at www.dragonfiresdk.net/forums
   Version 2.2.1
*/

#ifndef SCROLLER
#define SCROLLER

int nOldX;
int nOldY;
int nNewX;
int nNewY;

int nTouchId;
int nTouchEvent;

// This function will be called when a scroller is activated
int OnTouch(int id, int event, int x, int y)
{
// Get the ID of the container
nTouchId = id;

// Get the event of the container
nTouchEvent = event;

// Record initial coordinates when pressed down
if (event == 1)
{
nOldX = x;
nOldY = y;
nNewX = x;
nNewY = y;
}

// Record new coordinates when moving
if (event == 2)
{
nNewX = x;
nNewY = y;
}

// Erase coordinates when released
if (event == 3)
{
nOldX = 0;
nOldY = 0;
nNewX = 0;
nNewY = 0;
}

return id;
}

// This function approximates a container to it's target position.
void SlideContainer(int container, int targetX, int targetY, int speed)
{
/* To approximate the container, we're going to do the following:

   1. We get the container's x and y positions on the screen.
   2. If the value is less than the target value, we substract the
      value from the target value. If the value is more than the
  target value, we substract the target value from the value.
   3. We divide the difference by the speed.
   4. If the result equals 0, we make it equal to 1.
   5. If the value is less than the target value, we add the result
      to the value. If the value is more than the target value, we
  substract the result from the value.
   6. Finally, we set the container's position to our x and y values.
*/

int x = ContainerGetx(container);
int y = ContainerGety(container);

if (x < targetX)
{
int n = (targetX - x) / speed;
if (n == 0) n = 1;
x += n;
}
else if (x > targetX)
{
int n = (x - targetX) / speed;
if (n == 0) n = 1;
x -= n;
}

if (y < targetY)
{
int n = (targetY - y) / speed;
if (n == 0) n = 1;
y += n;
}
else if (y > targetY)
{
int n = (y - targetY) / speed;
if (n == 0) n = 1;
y -= n;
}

ContainerSetxy(container, x, y);
}

// This class creates a simple scroller for containers
class Scroller
{
/* A scroller lets a container slide upon a portion of the screen.
   It's designed to move containers that are too big for the screen,
   both vertically and horizontally, but it can be modified to move
   smaller objects too, such as sliders. This can be achieved by
   modifying minimum and maximum ranges, using the SetMinX, SetMinY,
   SetMaxX, and SetMaxY functions, or alternatively, using the
   SetBoundaries function, which modifies all of these values at once.

   The scroller uses the following components:

   1. A container which contains all of the elements in the scroller.
   2. A touch component which must be at least the size of the
      entire container, which records the drag and drop actions.
   3. An ID number that allows to differentiate among scrollers,
      since all scrollers use the same OnTouch function.
   
   The recoil component uses the following variables:

   1. Elasticity, defined here as the property of an object to return
      to its original state after a stress. In this particular context,
  elasticity can be thought of as how fast the container returns to
  its position.
 
  Note: The lower the value, the higher the elasticity!

   2. Compliance, defined here as the inverse of elasticity. In this
      particular context, compliance can be thought of as the resistance
  of a container to move out of position.

  Note: The higher the value, the higher the compliance!
   
   The friction component uses the following variables:

   1. Max Speed, defined here as the maximum sliding speed between two
      bodies, independent of the acceleration.

   2. Friction Coefficient, defined here as an empirical property of the
      containers which describes the force of friction between two bodies
  and the force pressing them together. For example, ice on steel has
  a low coefficient of friction, while rubber on pavement has a high
  coefficient of friction.

   3. Momentum, defined here as the property of containers to continue
      moving until stopped by a force of friction. The greater the
  momentum of a container, the stronger the force of friction that
  is required to stop the container.
*/

private:

int m_nContainer;
int m_nTouch;

int m_nX;
int m_nY;

int m_nMinX;
int m_nMinY;

int m_nMaxX;
int m_nMaxY;

int m_nCompliance;
int m_nElasticity;

int m_nSpeedX;
int m_nSpeedY;

int m_nMaxSpeed;
int m_nFrictionCoef;
int m_nMomentum;

int i;
int j;

int m_anTouchX[10000];
int m_anTouchY[10000];

bool m_bHorizontalScroll;
bool m_bVerticalScroll;

bool m_bRecoil;
bool m_bFriction;

bool m_bHorizontalSliding;
bool m_bVerticalSliding;

bool m_bActive;

public:

// Constructor
Scroller()
{

}

// This function initiates the scroller and all of its components
void Init(int& container, int touchWidth, int touchHeight, int id)
{
m_nContainer = container;
m_nTouch = TouchAdd(container, 0, 0, touchWidth, touchHeight, OnTouch, id);

m_nX = ContainerGetx(container);
m_nY = ContainerGety(container);

m_nMinX = -touchWidth+320;
m_nMinY = -touchHeight+480;

m_nMaxX = 0;
m_nMaxY = 0;

m_nCompliance = 10;
m_nElasticity = 3;

m_nSpeedX = 0;
m_nSpeedY = 0;

m_nMaxSpeed = 100;
m_nFrictionCoef = 3;
m_nMomentum = 2;

m_bHorizontalScroll = true;
m_bVerticalScroll = true;

m_bRecoil = true;
m_bFriction = true;

m_bHorizontalSliding = false;
m_bVerticalSliding = false;

m_bActive = true;
}

// This function updates the scroller
void Update()
{
/* To update the scroller we're going to:

   1. Check if the x coordinate has changed. If it has, if horizontal
      scrolling is allowed, and if the scroller is active, move the
  container. If the container is out of bounds, and recoil is
  allowed, move the container using compliance. If the container
  is out of bounds, and recoil is not allowed, move the container
  within bounds.

   2. Repeat for the y coordinate.

   3. If nothing has changed, then make the scroller x and y
      coordinates equal to the container's coordinates. This
  is necessary to start scrolling again. If the container
  is out of bounds, slide the container within bounds using
  elasticty.

   4. If friction is allowed, if pressing down, and if the scroller is
      active, set horizontal and vertical sliding off; if moving, store
  the x and y coordinates in an array; if pressing up, check if there
  is vertical or horizontal acceleration and if the acceleration
  surpasses the coefficient of friction. If there is, set sliding on.
   
   5. Vertical and horizontal speed is calculated as the last
      recorded x and y coordinates minus the next to last recorded
  coordinates. If speed exceeds maximum speed, set speed to
  maximum speed.

   6. If horizontal sliding is on, if horizontal scrolling is allowed,
      and if the coordinate is out of bounds, set the speed to zero.
  Otherwise, substract the horizontal speed from the x coordinate.
  Move the container horizontally. If the frame count is divisble
  by the momentum, and if speed is greater than zero, substract
  one from the speed, thus creating friction. If the speed is
  equal to zero, set horizontal sliding off.

   7. Repeat for vertical sliding.
*/

if (nOldX != nNewX && m_bHorizontalScroll && m_bActive)
{
ContainerSetx(m_nContainer, m_nX+nNewX-nOldX);

if (ContainerGetx(m_nContainer) < m_nMinX)
{
if (m_bRecoil)
ContainerSetx(m_nContainer, m_nMinX-(m_nMinX-(m_nX+nNewX-nOldX))/m_nCompliance);
else
ContainerSetx(m_nContainer, m_nMinX);
}

if (ContainerGetx(m_nContainer) > m_nMaxX)
{
if (m_bRecoil)
ContainerSetx(m_nContainer, m_nMaxX-(m_nMaxX-(m_nX+nNewX-nOldX))/m_nCompliance);
else
ContainerSetx(m_nContainer, m_nMaxX);
}
}

if (nOldY != nNewY && m_bVerticalScroll && m_bActive)
{
ContainerSety(m_nContainer, m_nY+nNewY-nOldY);

if (ContainerGety(m_nContainer) < m_nMinY)
{
if (m_bRecoil)
ContainerSety(m_nContainer, m_nMinY-(m_nMinY-(m_nY+nNewY-nOldY))/m_nCompliance);
else
ContainerSety(m_nContainer, m_nMinY);
}

if (ContainerGety(m_nContainer) > m_nMaxY)
{
if (m_bRecoil)
ContainerSety(m_nContainer, m_nMaxY-(m_nMaxY-(m_nY+nNewY-nOldY))/m_nCompliance);
else
ContainerSety(m_nContainer, m_nMaxY);
}
}

if (nOldX == nNewX && nOldY == nNewY)
{
m_nX = ContainerGetx(m_nContainer);
m_nY = ContainerGety(m_nContainer);

if (m_nX < m_nMinX)
SlideContainer(m_nContainer, m_nMinX, m_nY, m_nElasticity);
if (m_nY < m_nMinY)
SlideContainer(m_nContainer, m_nX, m_nMinY, m_nElasticity);
if (m_nX > m_nMaxX)
SlideContainer(m_nContainer, m_nMaxX, m_nY, m_nElasticity);
if (m_nY > m_nMaxY)
SlideContainer(m_nContainer, m_nX, m_nMaxY, m_nElasticity);
}

if (m_bFriction && m_bActive)
{
j++;

if (nTouchEvent == 1)
{
i = 0;
m_bHorizontalSliding = false;
m_bVerticalSliding = false;
}

if (nTouchEvent == 2)
{
i++;
m_anTouchX[i] = nNewX;
m_anTouchY[i] = nNewY;
}

if (nTouchEvent == 3)
{
j = 0;

if ((m_anTouchX[i] > m_anTouchX[i-1] && m_anTouchX[i] - m_anTouchX[i-1] > m_nFrictionCoef) ||
(m_anTouchX[i] < m_anTouchX[i-1] && m_anTouchX[i-1] - m_anTouchX[i] > m_nFrictionCoef))
m_bHorizontalSliding = true;

if ((m_anTouchY[i] > m_anTouchY[i-1] && m_anTouchY[i] - m_anTouchY[i-1] > m_nFrictionCoef) ||
(m_anTouchY[i] < m_anTouchY[i-1] && m_anTouchY[i-1] - m_anTouchY[i] > m_nFrictionCoef))
m_bVerticalSliding = true;

m_nSpeedX = m_anTouchX[i-1] - m_anTouchX[i];
m_nSpeedY = m_anTouchY[i-1] - m_anTouchY[i];

if (m_nSpeedX >= m_nMaxSpeed)
m_nSpeedX = m_nMaxSpeed;

if (m_nSpeedX <= -m_nMaxSpeed)
m_nSpeedX = -m_nMaxSpeed;

if (m_nSpeedY >= m_nMaxSpeed)
m_nSpeedY = m_nMaxSpeed;

if (m_nSpeedY <= -m_nMaxSpeed)
m_nSpeedY = -m_nMaxSpeed;

nTouchEvent = -1;
}

if (m_bHorizontalSliding && m_bHorizontalScroll)
{
if (m_nX < m_nMinX || m_nX > m_nMaxX)
m_nSpeedX = 0;
else
m_nX -= m_nSpeedX;

ContainerSetx(m_nContainer, m_nX);

if (j % m_nMomentum == 0 && m_nSpeedX > 0)
m_nSpeedX--;

if (j % m_nMomentum == 0 && m_nSpeedX < 0)
m_nSpeedX++;

if (m_nSpeedX == 0)
m_bHorizontalSliding = false;
}

if (m_bVerticalSliding && m_bVerticalScroll)
{
if (m_nY < m_nMinY || m_nY > m_nMaxY)
m_nSpeedY = 0;
else
m_nY -= m_nSpeedY;

ContainerSety(m_nContainer, m_nY);

if (j % m_nMomentum == 0 && m_nSpeedY > 0)
m_nSpeedY--;

if (j % m_nMomentum == 0 && m_nSpeedY < 0)
m_nSpeedY++;

if (m_nSpeedY == 0)
m_bVerticalSliding = false;
}
}
}

// This function sets the minimum x range
void SetMinX(int x)
{
m_nMinX = x;
}

// This function sets the minimum y range
void SetMinY(int y)
{
m_nMinY = y;
}

// This function sets the maximum x range
void SetMaxX(int x)
{
m_nMaxX = x;
}

// This function sets the maximum y range
void SetMaxY(int y)
{
m_nMaxY = y;
}

// This function sets the minimum and maximum x and y ranges
void SetBoundaries(int minX, int minY, int maxX, int maxY)
{
SetMinX(minX);
SetMinY(minY);
SetMaxX(maxX);
SetMaxY(maxY);
}

// This function sets the horizontal scroll
void SetHorizontalScroll(bool flag)
{
m_bHorizontalScroll = flag;
}

// This function sets the vertical scroll
void SetVerticalScroll(bool flag)
{
m_bVerticalScroll = flag;
}

// This function sets the recoil
void SetRecoil(bool flag)
{
m_bRecoil = flag;
}

// This function sets the friction
void SetFriction(bool flag)
{
m_bFriction = flag;
}

// This function sets the active state
void SetActive(bool flag)
{
m_bActive = flag;
}
};

#endif

ScrollButton.h
Code: [Select]
/* Copyright 2012 Juan José Sánchez Ramírez

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.

   Author Juan José Sánchez Ramírez
   Version 1.0
*/

#ifndef SCROLL_BUTTON
#define SCROLL_BUTTON

// This class creates a button for scrollers
class ScrollButton
{

private:

int m_nContainer;

int m_nId;

int m_nImage1;
int m_nImage2;

int m_nView;
int m_nTouch;

int (*m_pnCallback)(int id);

bool m_bScrolling;

bool m_bRelease;

public:

// Constructor
ScrollButton()
{

}

// This function initiates the list button and all of its components
void Init(int& container, char* filename, int x, int y, int (*callback)(int id), int id)
{
m_nContainer = container;

m_nId = id;

char name[100];

StrCopy(name, filename);
StrAppend(name, "1.png");

m_nImage1 = ImageAdd(name);

StrCopy(name, filename);
StrAppend(name, "2.png");

m_nImage2 = ImageAdd(name);

m_nView = ViewAdd(container, m_nImage1, x, y);

int width = ImageGetWidth(m_nImage1);
int height = ImageGetHeight(m_nImage1);

m_nTouch = TouchAdd(container, x, y, width, height, OnTouch, id);

m_pnCallback = callback;

m_bScrolling = false;

m_bRelease = false;
}

// This function updates the scroller
void Update()
{
if (nTouchId == m_nId)
{
if (nTouchEvent == 1)
{
ViewSetImage(m_nView, m_nImage2);
m_bRelease = false;
}
else
{
ViewSetImage(m_nView, m_nImage1);

if (nTouchEvent == 2)
m_bScrolling = true;

if ((nTouchEvent == 3 && !m_bRelease) || (nTouchEvent == -1 && !m_bRelease))
{
if (m_bScrolling)
m_bScrolling = false;
else
m_pnCallback(m_nId);

m_bRelease = true;
}
}
}
}
};

#endif

App.cpp
http://pastebin.com/pGhA41cy

Download example here:
http://www.mediafire.com/?2ipll7cb2zlxpie

These two classes work with containers, so you need DragonFireSDK v2.0 or above. Initiate every component in the AppMain. Update components in OnTimer. Make sure to give different IDs to every object!

Edit: Changed the code to version 2.2.1. Changed the example to the one in the video.
« Last Edit: October 03, 2012, 07:37:17 am by jjsanchezramirez »

Nico91

  • Guest
Re: Scrollers and Buttons
« Reply #1 on: October 02, 2012, 03:26:13 pm »
Good work. Is it possibile semplify the code?

jjsanchezramirez

  • Guest
Re: Scrollers and Buttons
« Reply #2 on: October 02, 2012, 04:29:00 pm »
I don't think so, but it runs perfectly. Try it out yourself. Besides, it's not very hard to use. I'm sure you wouldn't have problems with it.

jjsanchezramirez

  • Guest
Re: Scrollers and Buttons
« Reply #3 on: October 02, 2012, 10:47:05 pm »
I added a video.

http://www.youtube.com/watch?v=J1UELzjk0XE

It shows how you can use these two classes to create a working menu. I still have a small issue to work out, so I'm not pasting the demo code just yet. I need the code to move just one scroller at a time. Once I figure that out, you could theoretically have as many pages as you want. You have to link them manually, so I wouldn't recommend that.

PS: By the way, Tim and moderators, it would be nice if you could tag videos in BB code.

Edit: Okay, so I fixed the code, adding a boolean to tell apart when the scroller is active or inactive. It seems to be working perfectly. I'm just going to clean it up and I'll upload it tomorrow.

I don't think I ever was into game making. But I can definitely start making non game-related apps now.
« Last Edit: October 02, 2012, 11:17:23 pm by jjsanchezramirez »

Nico91

  • Guest
Re: Scrollers and Buttons
« Reply #4 on: October 03, 2012, 01:27:56 am »
But in the file for build center you can Add only App.cpp , do you Think that you can Add also file .h ?

jjsanchezramirez

  • Guest
Re: Scrollers and Buttons
« Reply #5 on: October 03, 2012, 01:48:04 am »
Nico, I must insist, you can also add header files. I've successfully compiled the code and tested it on my iPad. I think there are some threads in the forum regarding this specific issue. Look in the Tricks and Tips section. I believe Tim from DFSFK posted something there.

In C and C++, header files, generally speaking, are where you make your declarations for a .cpp file of the same name and for the "outside" world to consume.  You can also add implementation code in header files if you so choose.  In DragonFireSDK, since you can't include other .cpp files, but you can include additional .h files, this is a very useful solution for adding code modules to your projects.

Headers are useful for putting related code into discrete units as this helps with keeping your code organized.  If you were to simply lump everything together into a single file, it would not take long at all before the code in that file became unwieldy.

If you want your code to be able to call the code in a header file, you need to "#include" it.  Like so:

Code: [Select]
#include "MyGreatCode.h"

Hope this answers your questions!
« Last Edit: October 03, 2012, 01:56:24 am by jjsanchezramirez »

Nico91

  • Guest
Re: Scrollers and Buttons
« Reply #6 on: October 03, 2012, 02:00:34 am »
explain how they work. Inside the build folder I have to put both the. cpp and . h?

jjsanchezramirez

  • Guest
Re: Scrollers and Buttons
« Reply #7 on: October 03, 2012, 05:45:46 am »
Pretty much. You also have to include the header files in App.cpp, and you can't have any other .cpp files. Basically, you end up using header files as if they were .cpp files.

jjsanchezramirez

  • Guest
Re: Scrollers and Buttons
« Reply #8 on: October 03, 2012, 07:40:45 am »
I changed the example to the one in the video. As you can see, it's not too hard to use. It's also a very good example of how to create multiple pages.

Watch the video here:
http://www.youtube.com/watch?v=J1UELzjk0XE

App.cpp
http://pastebin.com/pGhA41cy

Download example here:
http://www.mediafire.com/?2ipll7cb2zlxpie

jjsanchezramirez

  • Guest
Re: Scrollers and Buttons
« Reply #9 on: October 04, 2012, 12:21:57 am »
Well, I'm a bit disappointed. I thought people would be a bit more excited about being able to create menus.  :P

I know DFSDK v3.0 will have incorporated graphic elements, but who knows when that's coming out. Besides, I really feel I did a good job making it feel natural, if you know what I mean.

Anyhow, there's the example. It's pretty much self-explanatory, but if anyone has any questions, feel free to ask.

As for me, I've decided I want to put all the code I've made this past year together in a series of educational apps for medicine students. Wish me luck!

nednones

  • Guest
Re: Scrollers and Buttons
« Reply #10 on: October 04, 2012, 12:47:13 am »
jjsanchezramirez don't be too hard on yourself. You've made some brilliant object based code for the scrollers which is something that DFSDK has needed for a long time particulary for the apps which require a more business/management style to them! Keep up the good work!

DeadSn0wMan

  • Guest
Re: Scrollers and Buttons
« Reply #11 on: October 13, 2012, 02:39:48 pm »
jjsanchezramirez This is awesome and you should be very proud of this work it's amazing how you got the natural feeling to work so smooth! This will really help me out in my app
« Last Edit: October 13, 2012, 02:41:52 pm by DeadSn0wMan »

jjsanchezramirez

  • Guest
Re: Scrollers and Buttons
« Reply #12 on: October 13, 2012, 07:33:32 pm »
Thank you! So far it's working fine.

DeadSn0wMan

  • Guest
Re: Scrollers and Buttons
« Reply #13 on: October 14, 2012, 03:30:13 am »
I'l think I need to imploment the scroller on the side if you know what I mean so the user can se how far he/she have scrolled

jjsanchezramirez

  • Guest
Re: Scrollers and Buttons
« Reply #14 on: October 14, 2012, 11:01:30 am »
It wouldn't be that hard, you know. You just need an image you move every frame.