usbloadergx/source/libwiigui/gui_element.cpp

820 lines
16 KiB
C++
Raw Blame History

/****************************************************************************
* libwiigui
*
* Tantric 2009
*
* gui_element.cpp
*
* GUI class definitions
***************************************************************************/
#include "gui.h"
/**
* Constructor for the Object class.
*/
//mutex_t GuiElement::mutex = LWP_MUTEX_NULL;
mutex_t GuiElement::_lock_mutex = LWP_MUTEX_NULL;
GuiElement::GuiElement()
{
xoffset = 0;
yoffset = 0;
zoffset = 0;
xmin = 0;
xmax = 0;
ymin = 0;
ymax = 0;
width = 0;
height = 0;
alpha = 255;
scale = 1;
state = STATE_DEFAULT;
stateChan = -1;
trigger[0] = NULL;
trigger[1] = NULL;
trigger[2] = NULL;
trigger[3] = NULL;
trigger[4] = NULL;
trigger[5] = NULL;
parentElement = NULL;
rumble = true;
selectable = false;
clickable = false;
holdable = false;
visible = true;
focus = -1; // cannot be focused
updateCB = NULL;
yoffsetDyn = 0;
xoffsetDyn = 0;
yoffsetDynFloat = 0;
alphaDyn = -1;
scaleDyn = 1;
effects = 0;
effectAmount = 0;
effectTarget = 0;
effectsOver = 0;
effectAmountOver = 0;
effectTargetOver = 0;
frequency = 0.0f;
changervar = 0;
degree = -90.0f;
circleamount = 360.0f;
Radius = 150;
angleDyn = 0.0f;
anglespeed = 0.0f;
// default alignment - align to top left
alignmentVert = ALIGN_TOP;
alignmentHor = ALIGN_LEFT;
// if(mutex == LWP_MUTEX_NULL) LWP_MutexInit(&mutex, true);
if(_lock_mutex == LWP_MUTEX_NULL) LWP_MutexInit(&_lock_mutex, true);
_lock_thread = LWP_THREAD_NULL;
_lock_count = 0;
_lock_queue = LWP_TQUEUE_NULL;
}
/**
* Destructor for the GuiElement class.
*/
GuiElement::~GuiElement()
{
// LWP_MutexDestroy(mutex);
}
void GuiElement::SetParent(GuiElement * e)
{
LOCK(this);
parentElement = e;
}
GuiElement * GuiElement::GetParent()
{
return parentElement;
}
/**
* Get the left position of the GuiElement.
* @see SetLeft()
* @return Left position in pixel.
*/
int GuiElement::GetLeft()
{
int x = 0;
int pWidth = 0;
int pLeft = 0;
if(parentElement)
{
pWidth = parentElement->GetWidth();
pLeft = parentElement->GetLeft();
}
if(effects & (EFFECT_SLIDE_IN | EFFECT_SLIDE_OUT | EFFECT_GOROUND | EFFECT_ROCK_VERTICLE))
pLeft += xoffsetDyn;
switch(alignmentHor)
{
case ALIGN_LEFT:
x = pLeft;
break;
case ALIGN_CENTRE:
x = pLeft + (pWidth/2) - (width/2);
break;
case ALIGN_RIGHT:
x = pLeft + pWidth - width;
break;
}
return x + xoffset;
}
/**
* Get the top position of the GuiElement.
* @see SetTop()
* @return Top position in pixel.
*/
int GuiElement::GetTop()
{
int y = 0;
int pHeight = 0;
int pTop = 0;
if(parentElement)
{
pHeight = parentElement->GetHeight();
pTop = parentElement->GetTop();
}
if(effects & (EFFECT_SLIDE_IN | EFFECT_SLIDE_OUT | EFFECT_GOROUND | EFFECT_ROCK_VERTICLE))
pTop += yoffsetDyn;
switch(alignmentVert)
{
case ALIGN_TOP:
y = pTop;
break;
case ALIGN_MIDDLE:
y = pTop + (pHeight/2) - (height/2);
break;
case ALIGN_BOTTOM:
y = pTop + pHeight - height;
break;
}
return y + yoffset;
}
void GuiElement::SetMinX(int x)
{
LOCK(this);
xmin = x;
}
int GuiElement::GetMinX()
{
return xmin;
}
void GuiElement::SetMaxX(int x)
{
LOCK(this);
xmax = x;
}
int GuiElement::GetMaxX()
{
return xmax;
}
void GuiElement::SetMinY(int y)
{
LOCK(this);
ymin = y;
}
int GuiElement::GetMinY()
{
return ymin;
}
void GuiElement::SetMaxY(int y)
{
LOCK(this);
ymax = y;
}
int GuiElement::GetMaxY()
{
return ymax;
}
/**
* Get the width of the GuiElement.
* @see SetWidth()
* @return Width of the GuiElement.
*/
int GuiElement::GetWidth()
{
return width;
}
/**
* Get the height of the GuiElement.
* @see SetHeight()
* @return Height of the GuiElement.
*/
int GuiElement::GetHeight()
{
return height;
}
/**
* Set the width and height of the GuiElement.
* @param[in] Width Width in pixel.
* @param[in] Height Height in pixel.
* @see SetWidth()
* @see SetHeight()
*/
void GuiElement::SetSize(int w, int h)
{
LOCK(this);
width = w;
height = h;
}
/**
* Get visible.
* @see SetVisible()
* @return true if visible, false otherwise.
*/
bool GuiElement::IsVisible()
{
return visible;
}
/**
* Set visible.
* @param[in] Visible Set to true to show GuiElement.
* @see IsVisible()
*/
void GuiElement::SetVisible(bool v)
{
LOCK(this);
visible = v;
}
void GuiElement::SetAlpha(int a)
{
LOCK(this);
alpha = a;
}
int GuiElement::GetAlpha()
{
int a;
if(alphaDyn >= 0)
a = alphaDyn;
else
a = alpha;
if(parentElement)
a *= parentElement->GetAlpha()/255.0;
return a;
}
float GuiElement::GetAngleDyn()
{
float a = 0.0;
if(angleDyn)
a = angleDyn;
if(parentElement && !angleDyn)
a = parentElement->GetAngleDyn();
return a;
}
void GuiElement::SetScale(float s)
{
LOCK(this);
scale = s;
}
float GuiElement::GetScale()
{
float s = scale * scaleDyn;
if(parentElement)
s *= parentElement->GetScale();
return s;
}
int GuiElement::GetState()
{
return state;
}
int GuiElement::GetStateChan()
{
return stateChan;
}
void GuiElement::SetState(int s, int c)
{
LOCK(this);
state = s;
stateChan = c;
}
void GuiElement::ResetState()
{
LOCK(this);
if(state != STATE_DISABLED)
{
state = STATE_DEFAULT;
stateChan = -1;
}
}
void GuiElement::SetClickable(bool c)
{
LOCK(this);
clickable = c;
}
void GuiElement::SetSelectable(bool s)
{
LOCK(this);
selectable = s;
}
void GuiElement::SetHoldable(bool d)
{
LOCK(this);
holdable = d;
}
bool GuiElement::IsSelectable()
{
if(state == STATE_DISABLED || state == STATE_CLICKED)
return false;
else
return selectable;
}
bool GuiElement::IsClickable()
{
if(state == STATE_DISABLED ||
state == STATE_CLICKED ||
state == STATE_HELD)
return false;
else
return clickable;
}
bool GuiElement::IsHoldable()
{
if(state == STATE_DISABLED)
return false;
else
return holdable;
}
void GuiElement::SetFocus(int f)
{
LOCK(this);
focus = f;
}
int GuiElement::IsFocused()
{
return focus;
}
void GuiElement::SetTrigger(GuiTrigger * t)
{
LOCK(this);
if(!trigger[0])
trigger[0] = t;
else if(!trigger[1])
trigger[1] = t;
else if(!trigger[2])
trigger[2] = t;
else if(!trigger[3])
trigger[3] = t;
else if(!trigger[4])
trigger[4] = t;
else if(!trigger[5])
trigger[5] = t;
else // both were assigned, so we'll just overwrite the first one
trigger[0] = t;
}
void GuiElement::SetTrigger(u8 i, GuiTrigger * t)
{
LOCK(this);
trigger[i] = t;
}
void GuiElement::RemoveTrigger(u8 i)
{
LOCK(this);
trigger[i] = NULL;
}
bool GuiElement::Rumble()
{
return rumble;
}
void GuiElement::SetRumble(bool r)
{
LOCK(this);
rumble = r;
}
int GuiElement::GetEffect()
{
LOCK(this);
return effects;
}
int GuiElement::GetEffectOnOver()
{
LOCK(this);
return effectsOver;
}
float GuiElement::GetFrequency()
{
LOCK(this);
return frequency;
}
void GuiElement::SetEffect(int eff, int speed, f32 circles, int r, f32 startdegree, f32 anglespeedset, int center_x, int center_y) {
if(eff & EFFECT_GOROUND) {
xoffsetDyn = 0; //!position of circle in x
yoffsetDyn = 0; //!position of circle in y
Radius = r; //!radius of the circle
degree = startdegree; //!for example -90 (<28>) to start at top of circle
circleamount = circles; //!circleamoutn in degrees for example 360 for 1 circle
angleDyn = 0.0f; //!this is used by the code to calc the angle
anglespeed = anglespeedset; //!This is anglespeed depending on circle speed 1 is same speed and 0.5 half speed
temp_xoffset = center_x; //!position of center in x
temp_yoffset = center_y; //!position of center in y
}
effects |= eff;
effectAmount = speed; //!Circlespeed
}
void GuiElement::SetEffect(int eff, int amount, int target)
{
LOCK(this);
if(eff & EFFECT_SLIDE_IN)
{
// these calculations overcompensate a little
if(eff & EFFECT_SLIDE_TOP)
yoffsetDyn = -screenheight;
else if(eff & EFFECT_SLIDE_LEFT)
xoffsetDyn = -screenwidth;
else if(eff & EFFECT_SLIDE_BOTTOM)
yoffsetDyn = screenheight;
else if(eff & EFFECT_SLIDE_RIGHT)
xoffsetDyn = screenwidth;
}
if(eff & EFFECT_FADE && amount > 0)
{
alphaDyn = 0;
}
else if(eff & EFFECT_FADE && amount < 0)
{
alphaDyn = alpha;
} else if(eff & EFFECT_ROCK_VERTICLE) {
changervar = 0;
yoffsetDyn = 0;
yoffsetDynFloat = 0.0;
}
effects |= eff;
effectAmount = amount;
effectTarget = target;
}
void GuiElement::SetEffectOnOver(int eff, int amount, int target)
{
LOCK(this);
effectsOver |= eff;
effectAmountOver = amount;
effectTargetOver = target;
}
void GuiElement::SetEffectGrow()
{
SetEffectOnOver(EFFECT_SCALE, 4, 110);
}
void GuiElement::StopEffect()
{
xoffsetDyn = 0;
yoffsetDyn = 0;
effects = 0;
effectsOver = 0;
effectAmount = 0;
effectAmountOver = 0;
effectTarget = 0;
effectTargetOver = 0;
scaleDyn = 1;
frequency = 0.0f;
changervar = 0;
//angleDyn = 0.0f;
anglespeed = 0.0f;
}
void GuiElement::UpdateEffects()
{
LOCK(this);
if(effects & (EFFECT_SLIDE_IN | EFFECT_SLIDE_OUT | EFFECT_GOROUND))
{
if(effects & EFFECT_SLIDE_IN)
{
if(effects & EFFECT_SLIDE_LEFT)
{
xoffsetDyn += effectAmount;
if(xoffsetDyn >= 0)
{
xoffsetDyn = 0;
effects = 0;
}
}
else if(effects & EFFECT_SLIDE_RIGHT)
{
xoffsetDyn -= effectAmount;
if(xoffsetDyn <= 0)
{
xoffsetDyn = 0;
effects = 0;
}
}
else if(effects & EFFECT_SLIDE_TOP)
{
yoffsetDyn += effectAmount;
if(yoffsetDyn >= 0)
{
yoffsetDyn = 0;
effects = 0;
}
}
else if(effects & EFFECT_SLIDE_BOTTOM)
{
yoffsetDyn -= effectAmount;
if(yoffsetDyn <= 0)
{
yoffsetDyn = 0;
effects = 0;
}
}
}
else
{
if(effects & EFFECT_SLIDE_LEFT)
{
xoffsetDyn -= effectAmount;
if(xoffsetDyn <= -screenwidth)
effects = 0; // shut off effect
}
else if(effects & EFFECT_SLIDE_RIGHT)
{
xoffsetDyn += effectAmount;
if(xoffsetDyn >= screenwidth)
effects = 0; // shut off effect
}
else if(effects & EFFECT_SLIDE_TOP)
{
yoffsetDyn -= effectAmount;
if(yoffsetDyn <= -screenheight)
effects = 0; // shut off effect
}
else if(effects & EFFECT_SLIDE_BOTTOM)
{
yoffsetDyn += effectAmount;
if(yoffsetDyn >= screenheight)
effects = 0; // shut off effect
}
}
}
if(effects & EFFECT_GOROUND) {
//!< check out gui.h for info
xoffset = temp_xoffset;
yoffset = temp_yoffset;
if(fabs(frequency) < circleamount) {
angleDyn = (frequency+degree+90.0f) * anglespeed;
xoffsetDyn = (int) lround(((f32) Radius)*cos((frequency+degree)*PI/180.0f));
yoffsetDyn = (int) lround(((f32) Radius)*sin((frequency+degree)*PI/180.0f));
frequency += ((f32) effectAmount)*0.01f;
} else {
f32 temp_frequency = ((effectAmount<0)?-1.0f:1.0f)*circleamount;
angleDyn = (temp_frequency+degree+90.0f) * anglespeed;
xoffsetDyn = (int) lround(((f32) Radius)*cos((temp_frequency+degree)*PI/180.0f));
yoffsetDyn = (int) lround(((f32) Radius)*sin((temp_frequency+degree)*PI/180.0f));
xoffset += xoffsetDyn;
yoffset += yoffsetDyn;
effects ^= EFFECT_GOROUND;
frequency = 0.0f;
}
}
if(effects & EFFECT_ROCK_VERTICLE) {
//move up to 10pixel above 0
if(changervar == 0 && yoffsetDynFloat < 11.0) {
yoffsetDynFloat += (effectAmount*0.01);
} else if(yoffsetDynFloat > 10.0) {
changervar = 1;
}
//move down till 10pixel under 0
if(changervar == 1 && yoffsetDynFloat > -11.0) {
yoffsetDynFloat -= (effectAmount*0.01);
} else if(yoffsetDynFloat < -10.0) {
changervar = 0;
}
yoffsetDyn = (int)(yoffsetDynFloat);
}
if(effects & EFFECT_FADE)
{
alphaDyn += effectAmount;
if(effectAmount < 0 && alphaDyn <= 0)
{
alphaDyn = 0;
effects = 0; // shut off effect
}
else if(effectAmount > 0 && alphaDyn >= alpha)
{
alphaDyn = alpha;
effects = 0; // shut off effect
}
}
if(effects & EFFECT_SCALE)
{
scaleDyn += effectAmount/100.0;
if((effectAmount < 0 && scaleDyn <= effectTarget/100.0)
|| (effectAmount > 0 && scaleDyn >= effectTarget/100.0))
{
scaleDyn = effectTarget/100.0;
effects = 0; // shut off effect
}
}
if(effects & EFFECT_PULSE)
{
int percent = 10; //go down from target by this
if((scaleDyn <= (effectTarget*0.01)) && (!changervar)) {
scaleDyn += (effectAmount*0.001);
} else if(scaleDyn > (effectTarget*0.01)) {
changervar = 1;
}
if((scaleDyn >= ((effectTarget-percent)*0.01)) && (changervar)) {
scaleDyn -= (effectAmount*0.001);
} else if(scaleDyn < ((effectTarget-percent)*0.01)) {
changervar = 0;
}
}
}
void GuiElement::Update(GuiTrigger * t)
{
LOCK(this);
if(updateCB)
updateCB(this);
}
void GuiElement::SetUpdateCallback(UpdateCallback u)
{
LOCK(this);
updateCB = u;
}
void GuiElement::SetPosition(int xoff, int yoff, int zoff)
{
LOCK(this);
xoffset = xoff;
yoffset = yoff;
zoffset = zoff;
}
void GuiElement::SetAlignment(int hor, int vert)
{
LOCK(this);
alignmentHor = hor;
alignmentVert = vert;
}
int GuiElement::GetSelected()
{
return -1;
}
/**
* Draw an element on screen.
*/
void GuiElement::Draw()
{
}
/**
* Draw Tooltips on screen.
*/
void GuiElement::DrawTooltip()
{
}
/**
* Check if a position is inside the GuiElement.
* @param[in] x X position in pixel.
* @param[in] y Y position in pixel.
*/
bool GuiElement::IsInside(int x, int y)
{
if(x > this->GetLeft() && x < (this->GetLeft()+width)
&& y > this->GetTop() && y < (this->GetTop()+height))
return true;
return false;
}
void GuiElement::Lock()
{
// LWP_MutexLock(mutex);
for(;;) // loop while element is locked by self
{
LWP_MutexLock(_lock_mutex);
if(_lock_thread == LWP_THREAD_NULL) // element is not locked
{
_lock_thread = LWP_GetSelf(); // mark as locked
_lock_count = 1; // set count of lock to 1
LWP_MutexUnlock(_lock_mutex);
return;
}
else if(_lock_thread == LWP_GetSelf()) // thread is locked by my self
{
_lock_count++; // inc count of locks;
LWP_MutexUnlock(_lock_mutex);
return;
}
else // otherwise the element is locked by an other thread
{
if(_lock_queue == LWP_TQUEUE_NULL) // no queue - meens it is the first access to the locked element
LWP_InitQueue(&_lock_queue); // init queue
LWP_MutexUnlock(_lock_mutex);
LWP_ThreadSleep(_lock_queue); // and sleep
// try lock again;
}
}
}
void GuiElement::Unlock()
{
// LWP_MutexUnlock(mutex);
LWP_MutexLock(_lock_mutex);
// only the thread was locked this element, can call unlock
if(_lock_thread == LWP_GetSelf()) // but we check it here <20> safe is safe
{
if(--_lock_count == 0) // dec count of locks and check if it last lock;
{
_lock_thread = LWP_THREAD_NULL; // mark as unlocked
if(_lock_queue != LWP_TQUEUE_NULL) // has a queue
{
LWP_CloseQueue(_lock_queue); // close the queue and wake all waited threads
_lock_queue = LWP_TQUEUE_NULL;
}
}
}
LWP_MutexUnlock(_lock_mutex);
}
SimpleLock::SimpleLock(GuiElement *e) : element(e)
{
element->Lock();
}
SimpleLock::~SimpleLock()
{
element->Unlock();
}